是否真的需要Delphi记录构造函数?

是否真的需要Delphi记录构造函数?,delphi,constructor,records,Delphi,Constructor,Records,形势 我正在学习Nick Hodges的“Delphi中的更多编码”,他正在使用一个TFraction记录来解释运算符重载。我自己写了这张唱片: type TFraction = record strict private aNumerator: integer; aDenominator: integer; function GCD(a, b: integer): integer; public constructor Create(aNumerat

形势

我正在学习Nick Hodges的“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;
正如您在这里看到的,我正在创建一个从函数返回的
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,然后调用构造函数,以便返回
t操作
。在第二种方法中,我没有创建任何内容,因为记录有一个自动构造函数。事实上,文件中说:

记录是使用默认的无参数自动构造的 构造函数,但类必须显式构造。因为 记录有一个默认的无参数构造函数,任何用户定义的 记录构造函数必须有一个或多个参数

这里我有一个用户定义的记录构造函数。因此:

  • 第一种方法的
    tmp
    上的构造函数调用是否不需要?如果我想调用
    Reduce
    (这是一个过程),我需要创建一个变量。
    结果
    是否只是返回一份
    tmp
    的副本而不创建任何内容

  • 在第二种方法中,
    Result.aNumerator
    Result.aDenominator
    是自动创建的构造函数的参数吗


  • 记录构造函数并不是什么神奇的东西。它和其他方法一样,只是一个实例方法。你写道:

    tmp := TFraction.Create(...);
    
    但你也可以这样写:

    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.Create(...);
    
    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;
    
    我个人认为这两种方法都不是特别有用,因为我习惯于为分配和默认初始化内存的类调用构造函数语义,然后调用构造函数方法

    尤其是第二个变体让我很恼火,因为这看起来像是新手Delphi程序员在开始创建类实例时犯的典型错误。如果
    t牵引
    是一个类,那么该代码将是不好的,但对于记录来说,它是好的

    如果是我,我将放弃记录构造函数,而是使用一个静态类函数返回一个新创建的记录类型实例。我的习惯是将这些东西命名为新的。但这些都是个人偏好的问题

    如果您这样做,它将被声明如下:

    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.Create(...);
    
    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;
    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.Create(...);
    
    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;
    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.Create(...);
    
    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;
    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.Create(...);
    
    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;
    

    我更喜欢构造函数方式,因为它对我来说更干净。Nick在做你建议我做的关于类函数的事情,所以我不知道结果。是否将其视为正态变量?我看你可以叫减价了!IMO建造商的记录具有误导性(一般而言)。没有办法确保它会被使用。记录是一个“值”,而不是一个对象。我在那一章中帮了尼克一点忙。似乎您知道结果是什么,因为在代码中使用它。当您分配给它时。它只是一个包含返回值的隐式var param。@delmo否。事实上,我回答中的代码演示了一个访问严格私有字段的类方法。在一个不相关的注释中,通常使用
    F
    作为类中私有字段的前缀,使用
    a
    作为方法参数的前缀(您确实使用的)。在Delphi中,这很好,但显然,在Lazarus中,这不会编译。好的,谢谢你,我不知道,我正在使用a作为参数和变量。我会改变我的习惯!@Jerry你的意思是什么。FPC不执行命名约定规则。@David@Jerry我不确定我是否相信。我怀疑Remy的评论有正确的信息。