2017-02-08 39 views
10

СИТУАЦИЯДействительно ли нужны конструкторы записей Delphi?

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

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; 

ПРОБЛЕМА

Поскольку оператор перегрузки возвращает 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. Во втором подходе я вместо этого ничего не создаю, потому что записи имеют автоматический конструктор. Документация, на самом деле, говорит, что:

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

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

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

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

+0

на несвязанной ноте, это, как правило, нормальная практика, чтобы использовать 'f' как префикс для частных полей в классе, и префикс' Ā' для параметров методы (который вы используете). В Delphi это прекрасно, но, видимо, в Лазаре, это не скомпилировалось. –

+0

Хорошо спасибо, я не знал, я использую параметры и переменные. Я изменю свою привычку! –

+0

@Jerry Что вы имеете в виду. FPC не применяет правила правил именования. –

ответ

9

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

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

Но вы одинаково хорошо может написать это:

tmp.Create(...); 

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

И особенно второй вариант решетки со мной, потому что это похоже на классическую ошибку, которую начинающие программисты 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; 
+0

Я предпочитаю путь конструктора, потому что мне кажется чище. Ник делает именно то, что вы предлагаете мне с помощью функции класса. –

+0

Также я не знал о результате. Рассматривается ли она как нормальная переменная? Я вижу, что вы можете назвать сокращение на нем! –

+3

Конструкторы ИМО по записям вводят в заблуждение (в общем). Нет способа убедиться, что он когда-либо будет использоваться. Запись - это значение, а не объект. –