Действительно ли нужны конструкторы Delphi record?

ситуация

я изучаю "больше кодирования в Delphi" Ника Ходжеса, и он использует TFraction запись для объяснения перегрузки оператора. Я сам написал эту запись:--17-->

type
  TFraction = record
  strict private
    aNumerator: integer;
    aDenominator: integer;
    function GCD(a, b: integer): integer;
  public
    constructor Create(aNumerator: integer; aDenominator: integer);
    procedure Reduce;
    class operator Add(fraction1, fraction2: TFraction): TFraction;
    class operator Subtract(fraction1, fraction2: TFraction): TFraction;
    //... implicit, explicit, multiply...
    property Numerator: integer read aNumerator;
    property Denominator: integer read aDenominator;
  end;

конечно, мне пришлось создать конструктор, потому что в Q (рациональные числа) я должен иметь знаменатель не равен нулю.

constructor TFraction.Create(aNumerator, aDenominator: integer);
begin
  if (aDenominator = 0) then
  begin
    raise Exception.Create('Denominator cannot be zero in rationals!');
  end;

  if ( (aNumerator < 0) or (aDenominator < 0) ) then
  begin
    Self.aNumerator := -aNumerator;
    Self.aDenominator := -aDenominator;
  end
  else
  begin
    Self.aNumerator := aNumerator;
    Self.aDenominator := aDenominator;
  end;
end;

так как перегрузка оператора возвращает a TFraction, Я собираюсь определить такую операцию:

class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
var
  tmp: TFraction;
begin
  //simple algorithm of the sum
  tmp := TFraction.Create(fraction1.Numerator*fraction2.Denominator+fraction1.Denominator*fraction2.Numerator, fraction1.Denominator*fraction2.Denominator);
  tmp.Reduce;

  //return the result
  Result := tmp;
end;

как вы можете видеть здесь, я создаю tmp это возвращается из функции.

class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
  Result.aNumerator := (fraction1.Numerator*fraction2.Denominator+fraction1.Denominator*fraction2.Numerator);
  Result.aDenominator := fraction1.Denominator*fraction2.Denominator;
end;
tmp и затем я вызываю конструктор, чтобы я мог возвратить TFraction. Во втором подходе я ничего не создаю, потому что записи имеют автоматический конструктор. В документации, по сути, говорится, что:

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

здесь у меня есть пользовательский конструктор записей. Итак:

  1. является вызов конструктора на tmp первого подхода не требуется? Если я захочу позвонить Reduce (процедура), мне нужно создать переменную. Это Result просто возвращаю копию tmp ничего не создавая?

  2. во втором подходе находятся Result.aNumerator и Result.aDenominator параметры автоматического созданного конструктора?

1 ответов


конструктор записей не является чем-то волшебным. Это просто метод экземпляра, как и любой другой. Ты пишешь:

tmp := TFraction.Create(...);

но вы также можете написать это так:

tmp.Create(...);

Я лично не считаю, что это особенно полезно, потому что я привык к конструктору, вызывающему семантику для классов, которые выделяют и инициализируют память по умолчанию, а затем вызывают метод конструктора.

и особенно второй вариант решетки со мной, потому что это выглядит как классическая ошибка, которую делают начинающие программисты Delphi при запуске и попытке создать экземпляр класса. Этот код не будет хорошим, если TFraction были класс, но для записи это нормально.

будь это я, я бы избавился от конструктора записи и вместо этого использовал статическую функцию класса, которая вернула недавно отчеканенный экземпляр вашего типа записи. Моя традиция-называть такие вещи New. Но это вопросы личного предпочтения.

если вы сделали что будет объявлено так:

class function New(aNumerator, aDenominator: Integer): TFraction; static;

это будет реализовано так:

class function TFraction.New(aNumerator, aDenominator: Integer): TFraction;
begin
  Result.aNumerator := ...;
  Result.aDenominator := ...;
end;

вы бы тогда назвали это так:

frac := TFraction.New(num, denom);

вы спрашиваете, можно ли пропустить конструктор. Что касается распределения записи, да, вы можете пропустить ее. С точки зрения запуска кода в конструкторе, только вы это можно определить. Вы хотите, чтобы этот код выполнялся или нет?

если вы хотите, чтобы этот код был выполнен, но не хотите использовать временную переменную, то вы можете написать код следующим образом:

class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
  Result.Create(
    fraction1.Numerator*fraction2.Denominator + fraction1.Denominator*fraction2.Numerator,
    fraction1.Denominator*fraction2.Denominator
  );
  Result.Reduce;
end;

или, если вы предпочитаете функцию статического класса, это будет:

class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
  Result := TFraction.New(
    fraction1.Numerator*fraction2.Denominator + fraction1.Denominator*fraction2.Numerator,
    fraction1.Denominator*fraction2.Denominator
  );
  Result.Reduce;
end;