在派生类中的构造函数声明后面,我需要添加 overload 或 override 这些词吗?

8

我有一个类层次结构,如下:

type
TMatrix = class
    protected
      //...
    public
      constructor Create(Rows, Cols: Byte);
    //...
type
  TMinMatrix = class(TMatrix)
    private
      procedure Allocate;
      procedure DeAllocate;
    public
      constructor Create(Rows, Cols: Byte);
      constructor CreateCopy(var that: TMinMatrix);
      destructor Destroy;
  end;

因此,您可以看到,派生类和基类的构造函数具有相同的参数列表。我从派生类显式调用基类构造函数:
constructor TMinMatrix.Create(Rows, Cols: Byte);
begin
   inherited;
   //...
end;

在Delphi中,是否有必要显式调用基类构造函数?也许我需要使用overload或override来清楚地表达我的意图?我知道在C++中如何做到这一点 - 只有当您想要将一些参数传递给基类构造函数时,才需要显式调用它 - 但我对Delphi编程没有太多经验。


我觉得所有的Delphi大师现在都在工作或睡觉,所以他们无法回答。 - chester89
今天我的电脑非常慢,我没法休息。 - Toon Krijthe
顺便提一下,我记得在我的一些旧代码中有一些非常难以追踪的错误是由于忘记显式调用继承构造函数引起的... :) - onnodb
4个回答

14

据我所知,这里涉及到两个独立的问题:

确保子类构造函数调用基类构造函数

您需要显式地调用基类的构造函数:

constructor TMinMatrix.Create(Rows, Cols: Byte);
begin
   inherited;
   //...
end;

确保子类构造函数覆盖基类构造函数

你还需要使子类构造函数使用override关键字,使基类构造函数使用virtual关键字,以确保编译器看到它们之间的关系。如果你不这样做,编译器可能会警告你TMinMatrix的构造函数“隐藏”了TMatrix的构造函数。因此,正确的代码应该是:

type
TMatrix = class
    protected
      //...
    public
      constructor Create(Rows, Cols: Byte); virtual;    // <-- Added "virtual" here
      //...
type
  TMinMatrix = class(TMatrix)
    private
      //...
    public
      constructor Create(Rows, Cols: Byte); override;   // <-- Added "override" here
      constructor CreateCopy(var that: TMinMatrix);
      destructor Destroy; override;                     // <-- Also make the destructor "override"!
  end;

请注意,您还应该使您的析构函数 override

介绍具有不同参数的构造函数

请注意,您只能使用相同参数列表覆盖构造函数。如果子类需要一个具有不同参数的构造函数,并且您想要防止直接调用基类的构造函数,则应编写:

type
TMyMatrix = class(TMatrix)
//...
public
  constructor Create(Rows, Cols, InitialValue: Byte); reintroduce; virtual;
//...
end

implementation

constructor TMyMatrix.Create(Rows, Cols, InitialValue: Byte);
begin
  inherited Create(Rows, Cols);   // <-- Explicitly give parameters here
  //...
end;

我希望这能让事情更加清晰...祝好运!


1
更正:普通构造函数而不是虚拟构造函数,您不需要在它们的后代上使用override,并且您不会收到有关未这样做的编译器警告。使用虚拟构造函数(带有override)的唯一(常见)情况是TComponent的后代。 - Mike Sutton
1
哦,是吗?嗯,那我一路上肯定错过了什么...所以,对于“普通”(非TComponent)类,您可以省略虚拟/覆盖的内容,仍然调用inherited,整个过程仍将正常工作? - onnodb
@MikeSutton-Mike,你关于TComponent构造函数的说法是正确的。然而,你会收到编译器错误提示:"E2137基类中未找到方法'Create'"。 - Gabriel

3

您需要显式调用继承的方法;Delphi不会为您执行此操作。 这是设计上的考虑,因为在某些情况下,您可能正在使用虚拟方法,例如,当您不想调用继承行为时。

另外,出于个人喜好的原因,我喜欢完全写出继承调用。 (inherited Create(Rows, Cols); 相对于只有inherited;,有一个简单的原因:它使代码遍历变得更加容易。如果您已经编写了方法调用,可以通过控制点击它并进入祖先方法。


3

Overload(重载),告诉编译器一个方法有相同的名称但参数不同。

Override(覆盖),告诉编译器一个方法覆盖了基类中声明为虚拟或动态的方法。

Reintroduce(重新引入),将隐藏在基类中声明为虚拟或动态的方法。

这些定义来自Ray Lischner的书《Delphi in a nutshell》。

type
  TFirst = class
  private
    FValue: string;
    FNumber: Integer;
  public
    constructor Create(AValue: string; ANumber: integer);

    property MyValue: string read FValue write FValue;
    property MyNumber: Integer read Fnumber write FNumber; 
  end;

  TSecond = class(TFirst)
  public
    constructor Create(AValue: string; ANumber: Integer);
  end;

constructor TFirst.Create(AValue: string; ANumber: integer);
begin
  MyValue := AValue;
  MyNumber := ANumber;
end;

{ TSecond }

constructor TSecond.Create(AValue: string; ANumber: Integer);
begin
  inherited;
end;

当声明TSecond时,它将调用TFirst的创建过程。如果没有继承,那么TSecond成员将保持为空。


2

如果两个构造函数的名称相同,则需要为两个构造函数进行重载。

type
  TMatrix = class
  protected
    //...
  public
    constructor Create(Rows, Cols: Byte);
    //...
type
  TMinMatrix = class(TMatrix)
  public
    constructor Create(Rows, Cols: Byte); overload;
    constructor Create(var that: TMinMatrix); overload;
  end;

调用继承的构造函数是一个好习惯。

constructor TMinMatrix.Create(Rows, Cols: Byte);
begin
   inherited Create(Rows, Cols); // Need to call the full name if the parameters are changed.
   //...
end;

可能是这样的:<code>inherited Create(Rows, Cols)</code>?或者参数列表是否无关紧要,只要派生类和基类中的列表相同? - chester89
如果参数列表相同,您可以省略它们。甚至可以省略继承构造函数的名称,并只需使用"inherited"而不是"inherited Create"。 - onnodb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接