Przeładowanie operatorów

Adam Boduch

Mechanizm przeładowania operatorów (lub inaczej ? przeciążania operatorów) był znany już wcześniej, m.in. programistom C++, a teraz został wprowadzony również do Delphi.

Mówiąc najogólniej, dzięki przeładowaniu operatorów można dokonywać różnych działań na obiektach klas (mnożenie, dodawanie, dzielenie itp.) tak samo jak na zmiennych, stałych, tym samym upraszczając nieco zapis kodu. Wygląda to tak: stosując operator (np. +) w obiektach klas, w rzeczywistości wywołujemy odpowiednią funkcję z klasy. Od projektanta zależy, jaki kod będzie miała owa funkcja. Działanie takie wygląda mniej więcej tak:

var
  X, Y : TMyClass;
begin
  X := 10;
  Y := X + X;
end;

Na początku ten mechanizm może wydawać się trochę dziwny, lecz osoby mające wcześniej styczność np. z C++ nie powinny mieć problemu ze zrozumieniem tego zagadnienia.

Operatory możliwe do przeładowania

W Delphi istnieje wiele operatorów, począwszy od operatorów arytmetycznych, a skończywszy na binarnych. O operatorze można powiedzieć także w przypadku funkcji Inc! Odpowiednikiem tego operatora w języku C jest ++, ale to nie zmienia faktu, że można przeładować także Inc.

W celu zadeklarowania operatora trzeba w rzeczywistości zadeklarować odpowiednią funkcję w klasie. W języku C++ wygląda to następująco:

class KLASA  
{ 
public: 
 KLASA operator+(int Value); 
}; 

Powyższy przypadek prezentuje sposób deklaracji operatora dodawania (+). Jest to dość czytelne ? używamy słowa kluczowego operator oraz odpowiedniego symbolu.

W Delphi wygląda to nieco inaczej ? należy w tym celu zadeklarować funkcję o odpowiedniej, z góry ustalonej nazwie ? np. Add. W takim przypadku w momencie dodawania do siebie dwóch klas w rzeczywistości zostanie wywołana funkcja Add. Lista operatorów wraz z ich odpowiednikami ? nazwami prezentuje tabela:

OperatorFunkcja odpowiadająca operatorowi
:=`Implicit(a : type) : resultType;`
[[Delphi/Inc]]`Inc(a: type) : resultType;`
[[Delphi/Dec]]`Dec(a: type): resultType;`
[[Delphi/not]]`LogicalNot(a: type): resultType;`
[[Delphi/Trunc]]`Trunc(a: type): resultType;`
[[Delphi/Round]]`Round(a: type): resultType;`
=`Equal(a: type; b: type) : Boolean;`
`NotEqual(a: type; b: type): Boolean;`
>`GreaterThan(a: type; b: type) Boolean;`
>=`GreaterThanOrEqual(a: type; b: type): resultType;`
<`LessThan(a: type; b: type): resultType;`
<=`LessThanOrEqual(a: type; b: type): resultType;`
+`Add(a: type; b: type): resultType;`
-`Subtract(a: type; b: type) : resultType;`
*`Multiply(a: type; b: type) : resultType;`
/`Divide(a: type; b: type) : resultType;`
[[Delphi/div]]`IntDivide(a: type; b: type): resultType;`
[[Delphi/mod]]`Modulus(a: type; b: type): resultType;`
[[Delphi/shl]]`ShiftLeft(a: type; b: type): resultType;`
[[Delphi/shr]]`ShiftRight(a: type; b: type): resultType;`
[[Delphi/and]]`LogicalAnd(a: type; b: type): resultType;`
[[Delphi/or]]`LogicalOr(a: type; b: type): resultType;`
[[Delphi/xor]]`LogicalXor(a: type; b: type): resultType;`
[[Delphi/and]]`BitwiseAnd(a: type; b: type): resultType;`
[[Delphi/or]]`BitwiseOr(a: type; b: type): resultType;`
[[Delphi/xor]]`BitwiseXor(a: type; b: type): resultType;`

Deklaracja operatorów

Podsumujmy: aby można było użyć przeładowania operatora, np. :=, należy zadeklarować w klasie odpowiednią funkcję (w tym przypadku ? Implicit). Konstrukcja ta jest dość specyficzna:

  TOverload = class
    class operator Implicit(Value : Integer) : TOverload;
  end;

Kluczową rolę odgrywa tutaj słowo kluczowe operator. Należy zwrócić uwagę, że chociaż jest to funkcja, to jednak brak słowa kluczowego ? Function. Należy za to użyć konstrukcji class operator. Dodatkowo taka ?funkcja? musi zwracać zawsze typ odpowiadający nazwie klasy. Czyli w klasie TOverload operator musi zwracać typ TOverload.

Zadeklarowałem właśnie funkcję Implicit, która jest przeładowanym operatorem. Od tej pory można w kodzie użyć następującej frazy:

procedure TWinForm2.Button1_Click(sender: System.Object; e: System.EventArgs);
var
  X : TOverload;
begin
  X := 10;
end;

{ TOverload }

class operator TOverload.Implicit(Value: Integer): TOverload;
begin
 // kod
end;

W normalnych warunkach kompilator nie pozwoliłby na skompilowanie kodu, w którym do obiektu typu TOverload próbujemy przypisać wartość liczbową (Integer). Jednak dzięki takiej konstrukcji w rzeczywistości wartość liczbowa (liczba 10) zostanie przekazana jako parametr do funkcji Implicit.

Naturalnie parametrem funkcji Implicit może być wartość innego typu ? np. String, Real itp. Wszystko zależy od aktualnych potrzeb.

Binary i Unary

Przeładowane operatory mogą kwalifikować się do dwóch kategorii: binary oraz unary. Różnica leży w liczbie elementów (parametrów) owej funkcji. Przykładowo, funkcja Implicit należy do kategorii unary, gdyż ma jeden parametr. Funkcja Add z kolei musi otrzymać dwa parametry i należy do kategorii binary.

1 komentarz

probuje przeladowac opeator dla mojej klasy,
robie dokladnie tak jak pisze (jest napisane:P) autor ale kompilator zwraca blad
z komunikatem ze po slowie class musi byc PROCEDURE, FUNCTION, PROPERTY albo VAR
no i nie daje sie skompilowac...
Jakies sugestie co mam zrobic zeby zadzialalo?