15.3 Операторы присваивания

На верх  Назад  Вперёд

Оператор присваивания определяет действие присвоения переменной одного типа другой (переменной). Тип результата должен совпадать с типом переменной в левой части оператора присваивания, единственный параметр оператора присваивания должен иметь тот же тип, что и выражение справа от оператора присваивания.

Эта система может быть использована, чтобы объявить новый тип, и определить присвоение для этого типа. Например, чтобы иметь возможность присваивать вновь определенный тип 'Complex'

Var

   C,Z : Complex; // Определённый ранее тип complex

begin

   Z:=C; // присвоение одной переменной типа complex другой.

end;

Должен быть определен следующий оператор присваивания:

Operator := (C : Complex) z : complex;

Для того, чтобы присвоить тип real сложному типу (complex) можно действовать следующим образом:

var

  R : real;

  C : complex;

begin

  C:=R;

end;

Должен быть определен следующий оператор присваивания:

Operator := (r : real) z : complex;

Таким образом определяется оператор присваивания (:=) с переменной типа real в правой части (аргумент оператора) и complex в левой части (результат оператора) выражения.

Пример реализации может быть следующим:

operator := (r : real) z : complex;

begin

  z.re:=r;

  z.im:=0.0;

end;

В данном примере, идентификатор результата (z) используется для хранения результата выполнения операции. При компиляции в режимах Delphi или ObjFPC, допускается использование идентификатора Result, или может быть заменен на Z (как идентификатор результата), эти два подхода эквивалентны (как показано выше).

operator := (r : real) z : complex;

begin

  Result.re:=r;

  Result.im:=0.0;

end;

Оператор присваивания используется и для преобразования типов (из одного типа в другой). Компилятор будет просматривать все перегружена операторы присваивания, пока не найдёт соответствия типов с левой и правой стороны выражения. Если такой оператор не найден, генерируется ошибка 'несоответствие типов' ('type mismatch').

Замечание:

Оператор присваивания не является коммутативным; компилятор не меняет типов двух аргументов (источника и назначения). Другими словами, если используется приведенное выше определение оператора присваивания, следующее не возможно:

var

  R : real;

  C : complex;

begin

  R:=C;

end;

Если обратное присвоение должно быть возможно, то оператор присваивания также должен быть определен. (Это не так для вещественных и комплексных чисел.)

Замечание:

Оператор присваивания также используется для неявного преобразования типов. Это может иметь нежелательные последствия. Рассмотрим следующие определения:

operator := (r : real) z : complex;

function exp(c : complex) : complex;

Тогда следующее присваивание даст несоответствие типов:

Var

  r1,r2 : real;

begin

  r1:=exp(r2);

end;

Несоответствие происходит потому, что компилятор будет сталкиваться с определением функции ехр с комплексным аргументом. Компилятор неявно преобразует r2 в complex, поэтому он может использовать описанную выше функцию exp. Результатом этой функции является тип complex, который не может быть назначен на r1, поэтому компилятор выдаст сообщение об ошибке 'несоответствия типов'. Компилятор не будет дальше смотреть на следущую функцию ехр , которая имеет уже правильные аргументы.

Можно избежать этой проблемы, путём уточнения

r1:=system.exp(r2);

При выполнении явного приведение типов, компилятор будет пытаться выполнить неявное преобразование, если присутствует оператор присваивания. Что означает:

Var

  R1 : T1;

  R2 : T2;

begin

R2:=T2(R1);

Будет осуществляться оператором

Operator := (aRight: T1) Res: T2;

Оператор будет переопределен, и затем он будет использоваться для объявленного типа вместо оператора по умолчанию.

Обратное не верно: В случае обычного присваивания, компилятор не будет рассматривать явные операторы присваивания.

Учитывая следующие определения:

uses

  sysutils;

 

type

  TTest1 = record

    f: LongInt;

  end;

  TTest2 = record

    f: String;

  end;

  TTest3 = record

    f: Boolean;

  end;

Можно создать операторы присваивания:

operator := (aRight: TTest1) Res: TTest2;

begin

  Writeln('Неявное TTest1 => TTest2');

  Res.f := IntToStr(aRight.f);

end;

 

operator := (aRight: TTest1) Res: TTest3;

begin

  Writeln('Неявное TTest1 => TTest3');

  Res.f := aRight.f <> 0;

end;

Но можно также определить тип оператора:

operator Explicit(aRight: TTest2) Res: TTest1;

begin

  Writeln('Явное TTest2 => TTest1');

  Res.f := StrToIntDef(aRight.f, 0);

end;

 

operator Explicit(aRight: TTest1) Res: TTest3;

begin

  Writeln('Явное TTest1 => TTest3');

  Res.f := aRight.f <> 0;

end;

Таким образом, следующий код

var

  t1: TTest1;

  t2: TTest2;

  t3: TTest3;

begin

  t1.f := 42;

  // Неявное

  t2 := t1;

   // теоретически явная, но будет использоваться неявная операция,

   // потому что никакого явного оператора не определено

  t2 := TTest2(t1);

  // следующий код не будет компилировать,

  // т.к. не определён ни один оператор присваивания

  //(явное присваивание здесь использоваться не будет)

  //t1 := t2;

  // Явное

  t1 := TTest1(t2);

   // Первое преобразование явное (TTest2 => TTest1),

   // следующее преобразование неявное (TTest1 => TTest3)

  t3 := TTest1(t2);

  // Неявное

  t3 := t1;

  // Явное

  t3 := TTest3(t1);

end.

будет выводить:

Неявное TTest1 => TTest2
Неявное TTest1 => TTest2
Явное TTest2 => TTest1
Явное TTest2 => TTest1
Неявное TTest1 => TTest3
Неявное TTest1 => TTest3
Явное TTest1 => TTest3