通过类变量构造对象是否总是需要父类具有构造函数?

时间:2017-08-08 11:41:52

标签: class delphi constructor delphi-xe2

我知道,这是一个令人费解的问题,我确信有人可以将其简化为基础。

请考虑以下代码:

TTestClass = class
public
end;

TTestClassDescendant = class(TTestClass)
public
  constructor Create;
end;


implementation

procedure TForm1.Button1Click(Sender: TObject);
var tc: TTestClass;
begin
  tc := TTestClassDescendant.Create;
  tc.Free;
end;

{ TTestClassDescendant }

constructor TTestClassDescendant.Create;
begin
  ShowMessage('Create executed')  // this gets executed
end;

Create过程正确执行。

现在考虑以下代码:

TTestClass = class
public
end;

TTestClassDescendant = class(TTestClass)
public
  constructor Create;
end;

TTestClassClass = class of TTestClass;

implementation

procedure TForm1.Button1Click(Sender: TObject);
var tc: TTestClass;
    tcc: TTestClassClass;
begin
  tcc := TTestClassDescendant;
  tc := tcc.Create;
  tc.Free
end;

{ TTestClassDescendant }

constructor TTestClassDescendant.Create;
begin
  ShowMessage('Create executed')  // this does NOT get executed
end;

后代类的Create过程不再执行。

但是,如果我在父类中引入一个构造函数并在子类中重写它,它就会被执行:

TTestClass = class
public
  constructor Create; virtual;
end;

TTestClassDescendant = class(TTestClass)
public
  constructor Create; override;
end;

请原谅我,如果我忽略了显而易见的事情,但是当构造通过类变量发生时,第二个代码块中的构造函数代码不应该被执行,就像通过类标识符本身调用它时一样?

1 个答案:

答案 0 :(得分:5)

  

请原谅我,如果我忽略了明显的,但不应该   构造函数代码在第二个代码块中执行时   构造通过类变量发生,就像它一样   通过类标识符本身调用?

不,它不应该。

宣言是

TTestClassClass = class of TTestClass; // note: of TTestClass!

这就是基础TTestClass(它继承自TObject)的(空)构造函数被调用的原因,因为那是 声明的 < / strong> a类TTestClassClass指的是。

如果您想要调用 实际 构造函数,那么您应该在基类中构建虚拟覆盖在后代,就像你在问题的最后部分所做的那样。

FWIW,如果你宣布

TTestClassDescendantClass = class of TTestClassDescendant;

然后使用它来实例化一个后代类,那么你应该确实得到一个TTestClassDescendant,构造函数应该显示你所期望的。

类比

但是构造函数的这种行为就像其他非虚拟和虚拟方法一样:

type
  TBase = class
    procedure DoSomething; // outputs: "TBase: Doing something"
  end;

  TDesc = class(TBase)
    procedure DoSomething; // outputs: "Descendant does it now"
  end;

var
  D: TBase;
begin
  D := TDesc.Create;
  D.DoSomething;

由于D被声明为TBase,因此对D.DoSomething的调用将调用TBase.DoSomething,而不是TDesc.DoSomething

但如果DoSomethingTDesc 虚拟重写,则D中的实际类}} 将会被使用。您提供的示例与此相同,只是您在那里使用元类。