суббота, 19 мая 2012 г.

Перегрузка операторов

Перевод раздела Operator Overloading из справочной системы Delphi

О перегрузке операторов

Delphi для Win32 разрешает перегрузку некоторых функций или "операторов" внутри объявления записей. Имя функции оператора сопоставляется в программном коде с его символьным представлением. Например, оператор Add сопоставляется с символом + . Компилятор генерирует вызов соответствующего перегружаемого оператора, сопоставляя контекст (то есть возвращаемый тип и тип параметров, использованных при вызове) с сигнатурой функции оператора.


Следующая таблица показывает операторы языка Delphi, которые могут быть перегружены:

ОператорКатегорияСигнатура объявленияСовмещаемый символ
ImplicitConversionImplicit(a : type) : resultType;implicit typecast
ExplicitConversionExplicit(a: type) : resultType;explicit typecast
NegativeUnaryNegative(a: type) : resultType;-
PositiveUnaryPositive(a: type): resultType;+
IncUnaryInc(a: type) : resultType;Inc
DecUnaryDec(a: type): resultTypeDec
LogicalNotUnaryLogicalNot(a: type): resultType;not
TruncUnaryTrunc(a: type): resultType;Trunc
RoundUnaryRound(a: type): resultType;Round
InSetIn(a: type; b: type) : Boolean;in
EqualComparisonEqual(a: type; b: type) : Boolean;=
NotEqualComparisonNotEqual(a: type; b: type): Boolean;<>
GreaterThanComparisonGreaterThan(a: type; b: type) Boolean;>
GreaterThanOrEqualComparisonGreaterThanOrEqual(a: type; b: type): Boolean;>=
LessThanComparisonLessThan(a: type; b: type): Boolean;<
LessThanOrEqualComparisonLessThanOrEqual(a: type; b: type): Boolean;<=
Add BinaryAdd(a: type; b: type): resultType;+
SubtractBinarySubtract(a: type; b: type) : resultType;-
MultiplyBinaryMultiply(a: type; b: type) : resultType;*
DivideBinaryDivide(a: type; b: type) : resultType;/
IntDivideBinaryIntDivide(a: type; b: type): resultType;div
ModulusBinaryModulus(a: type; b: type): resultType;mod
LeftShiftBinaryLeftShift(a: type; b: type): resultType;shl
RightShiftBinaryRightShift(a: type; b: type): resultType;shr
LogicalAndBinaryLogicalAnd(a: type; b: type): resultType;and
LogicalOrBinaryLogicalOr(a: type; b: type): resultType;or
LogicalXorBinaryLogicalXor(a: type; b: type): resultType;xor
BitwiseAndBinaryBitwiseAnd(a: type; b: type): resultType;and
BitwiseOrBinaryBitwiseOr(a: type; b: type): resultType;or
BitwiseXorBinaryBitwiseXor(a: type; b: type): resultType;xor

Операторы, не перечисленные в этой таблице, не могут быть использованы для работы с классами или записями. Методы перегруженных операторов не могут вызываться по имени в программном коде. Для доступа к методу оператора в определенном классе или записи см. Code Example:OpOverloads_(Delphi). Идентификаторы операторов, включенные в список элементов класса или записи, начинаются с ключевого слова "operator" (пример:System.AnsiStringBase_Functions). Вы можете реализовать любой из вышеперечисленных операторов в собственных классах и записях.

Компилятор будет использовать для класса или записи оператор в том случае если:

  • Для бинарных операторов: один из входных параметров должен быть классом;
  • Для унарных операторов классом должен быть либо входной параметр, либо возвращаемое значение;
  • Для логического оператора и побитового оператора, использующих один и тот же символ, логический оператор используется только когда операнды относятся к булевскому типу. Если тип класса этого оператора класса не является булевским, логический оператор будет использован только если другой операнд является булевским.

Для распределительных или коммутативных свойств операции не делается предположений. Для бинарных операторов первым параметром является всегда левый операнд, а правый – вторым. Предполагается ассоциативность в порядке слева-направо при отсутствии явно указанных скобок.

Разрешение методов операторов выполняется через объединение доступных операторов для типов, использованных в операции (включая наследуемые операторы). Для операции, включающей два различных типа A и B: если тип A может быть неявно преобразован к В, а В может быть неявно преобразован к А, будет иметь место неоднозначная интерпретация. Неявные преобразования должны использоваться только там, где они абсолютно необходимы, а рефлексивность должна быть исключена. Наилучшим решением будет разрешить типу В неявно преобразовать себя в тип А, позволив типу А ничего не знать о типе В (или наоборот).

Основным правилом является то, что операторы не должны изменять операнды, а возвращать новое значение, созданное при работе с параметрами.

Перегружаемые операторы особенно часто используются в записях (то есть в значимых типах).


Объявление перегрузки операторов

Перегрузка операторов объявляется внутри классов или записей со следующим синтаксисом:

type
   typeName = record
       class operator conversionOp(a: type): resultType;
       class operator unaryOp(a: type): resultType;
       class operator comparisonOp(a: type; b: type): Boolean;
       class operator binaryOp(a: type; b: type): resultType;
   end;

Реализация перегружаемых операторов также должна включать этот синтаксис:

class operator typeName.conversionOp(a: type): resultType;
class operator typeName.unaryOp(a: type): resultType;
class operator typeName.comparisonOp(a: type; b: type): Boolean;
class operator typeName.binaryOp(a: type; b: type): resultType;

Далее приведены некоторые примеры перегруженных операторов:

type
   TMyRecord = record
     class operator Add(a, b: TMyRecord): TMyRecord;// Добавление двух операндов типа TMyRecord
     class operator Subtract(a, b: TMyRecord): TMyRecord;// Вычитание типов TMyRecord
     class operator Implicit(a: Integer): TMyRecord;// Неявное преобразование Integer к TMyRecord
     class operator Implicit(a: TMyRecord): Integer;// Неявное преобразование TMyRecord к Integer
     class operator Explicit(a: Double): TMyRecord;// Явное преобразование Double к TMyRecord
   end;

// Пример реализации Add
class operator TMyRecord.Add(a, b: TMyRecord): TMyRecord;
begin
   // ...
end;

var
x, y: TMyRecord;
begin
   x := 12;      // Неявное преобразование к Integer
   y := x + x;   // ВызовTMyRecord.Add(a, b: TMyRecord): TMyRecord
   b := b + 100; // Вызов TMyRecord.Add(b, TMyRecord.Implicit(100))
end;

3 комментария:

  1. Спасибо, а внутри класса можно как-нибудь реализовать это?

    ОтветитьУдалить
  2. Я то думаю чего вы ссылку не даете на embarcaderoвский мануал, а вы с него примеры с катали. Очень глупо, но хотелось бы чтобы вы опомнились и свои рабочие и наглядные примеры написали.

    ОтветитьУдалить
  3. x := 12; // Неявное преобразование к Integer

    Здесь ошибка перевода хелпа
    x := 12; // Implicit conversion from an Integer

    Преобразование из Integer

    ОтветитьУдалить