通过class变量构造对象是否总是需要父class有构造函数?
Does the construction of an object through a class variable always need the parent class to have a constructor?
我知道这是一个令人费解的问题,我相信有人会在手边将其简化为基础知识。
考虑以下代码:
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;
创建过程正确执行。
现在考虑以下代码:
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;
后代 class 的创建过程不再执行。
但是,如果我在父 class 中引入一个构造函数并在后代 class 中覆盖它,它会被执行:
TTestClass = class
public
constructor Create; virtual;
end;
TTestClassDescendant = class(TTestClass)
public
constructor Create; override;
end;
请原谅我,如果我忽略了显而易见的,但是当通过 class 变量进行构造时,第二个代码块中的构造函数代码不应该被执行,就像它是通过 class 标识符本身调用?
Pardon me if I'm overlooking the obvious, but shouldn't the
constructor code in that second block of code be executed when the
construction occurs through a class variable, just as it is when it is
called through the class identifier itself?
不,不应该。
声明是
TTestClassClass = class of TTestClass; // note: of TTestClass!
这就是为什么基 TTestClass
(它继承自 TObject
)的(空)构造函数被调用的原因,因为那是 声明class一个TTestClassClass
指的是.
如果要调用 actual 构造函数,则应将构造函数设置为 virtual 在基础 class 和 override 在后代中,就像你在问题的最后一部分所做的那样。
FWIW,如果你声明一个
TTestClassDescendantClass = class of TTestClassDescendant;
然后用它来实例化一个后代 class,那么你确实应该得到一个 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
。
但是如果 DoSomething
在 TDesc
中是 虚拟的 和 覆盖的 ,那么 D
中的实际 class 将被使用。您给出的示例的工作原理相同,只是您在那里使用了 metaclasses。
我知道这是一个令人费解的问题,我相信有人会在手边将其简化为基础知识。
考虑以下代码:
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;
创建过程正确执行。
现在考虑以下代码:
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;
后代 class 的创建过程不再执行。
但是,如果我在父 class 中引入一个构造函数并在后代 class 中覆盖它,它会被执行:
TTestClass = class
public
constructor Create; virtual;
end;
TTestClassDescendant = class(TTestClass)
public
constructor Create; override;
end;
请原谅我,如果我忽略了显而易见的,但是当通过 class 变量进行构造时,第二个代码块中的构造函数代码不应该被执行,就像它是通过 class 标识符本身调用?
Pardon me if I'm overlooking the obvious, but shouldn't the constructor code in that second block of code be executed when the construction occurs through a class variable, just as it is when it is called through the class identifier itself?
不,不应该。
声明是
TTestClassClass = class of TTestClass; // note: of TTestClass!
这就是为什么基 TTestClass
(它继承自 TObject
)的(空)构造函数被调用的原因,因为那是 声明class一个TTestClassClass
指的是.
如果要调用 actual 构造函数,则应将构造函数设置为 virtual 在基础 class 和 override 在后代中,就像你在问题的最后一部分所做的那样。
FWIW,如果你声明一个
TTestClassDescendantClass = class of TTestClassDescendant;
然后用它来实例化一个后代 class,那么你确实应该得到一个 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
。
但是如果 DoSomething
在 TDesc
中是 虚拟的 和 覆盖的 ,那么 D
中的实际 class 将被使用。您给出的示例的工作原理相同,只是您在那里使用了 metaclasses。