Действительно ли нужны конструкторы 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 по умолчанию конструктор, но классы должны быть явно построены. Потому что записи имеют конструктор без аргументов по умолчанию, любой определяемый пользователем запись конструктор должен иметь один или несколько параметров.
здесь у меня есть пользовательский конструктор записей. Итак:
является вызов конструктора на
tmp
первого подхода не требуется? Если я захочу позвонитьReduce
(процедура), мне нужно создать переменную. ЭтоResult
просто возвращаю копиюtmp
ничего не создавая?во втором подходе находятся
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;