我对Delphi中提供的虚拟构造函数没有任何经验。我考虑在类层次结构中使用虚拟ctors来将实例重置为初始状态,如下所示:
A = class
end;
B = class(A)
end;
C = class(B)
end;
FooA = class
a_ : A;
constructor Create(inst : A); overload;
constructor Create; overload; virtual; abstract;
destructor Destroy; override;
function Bar : A;
end;
FooB = class(FooA)
b_ : B;
constructor Create; override;
constructor Create(inst : B); overload;
end;
FooC = class(FooB)
// ...
end;
{ FooA }
constructor FooA.Create(inst: A);
begin
inherited Create;
a_ := inst;
end;
destructor FooA.Destroy;
begin
FreeAndNil(a_);
inherited;
end;
function FooA.Bar : A;
begin
Result := a_;
a_ := nil;
// here comes the magic
Self.Create;
end;
{ FooB }
constructor FooB.Create;
begin
b_ := B.Create;
inherited Create(b_);
end;
constructor FooB.Create(inst: B);
begin
inherited Create(inst);
b_ := inst;
end;
{ FooC } // ...
var
fc : FooA;
baz : A;
begin
fc := FooC.Create;
baz := fc.Bar;
WriteLn(baz.ClassName);
FreeAndNil(baz);
FreeAndNil(fc);
ReadLn;
end.
此设计中是否存在任何问题/陷阱?这个简单的例子就像一个魅力,但我觉得有点不安的调用构造函数(不构造任何东西)就像这样。
编辑:
我决定将初始化移动到受保护区域中具有有意义名称的方法,这让我感觉更好; - )
FooA = class
strict private
a_ : A;
strict protected
procedure SetInst; overload; virtual; abstract;
procedure SetInst(i : A); overload;
public
constructor Create;
destructor Destroy; override;
function Foo : A;
end;
答案 0 :(得分:3)
编写很少的类来支持使用构造函数作为重新初始化程序。他们通常假设任何动态分配的内存已经分配了 not 。如果你控制了你正在使用的所有类,那么请继续并小心地使用构造函数作为重新初始化器。
即使你处于控制之中,我仍然会反对它。 这不是惯用的Delphi ;阅读你的代码的任何人(甚至你,从现在开始的几个星期或几个月)都会被你的非标准使用构造函数混淆 - 至少在开始时。这不值得麻烦。如果调用Bar
函数应该释放A
对象的所有权并创建一个新实例,那么编写具有明确名称的函数。
答案 1 :(得分:1)
Rob认为这是一个非常奇怪的代码,可能会让人感到困惑,并且将代码转移到初始化例程是一个好主意。如果您想知道,虚拟构造函数的主要目的是完全不同的东西:更容易支持“工厂”样式对象创建。
某些外部源提供了一些可以识别基类后代的数据,并且工厂使用类引用并调用基类中定义的虚拟构造函数。这样你最终得到了一个后代类的实例,而不必将后代类的知识硬编码到工厂代码中。
如果这听起来有点奇怪,请查看DFM文件。它有一个从TComponent下载的表单对象列表及其发布的属性。当表单读取代码遇到object
语句时,它会读取类名,在将类名映射到类引用的表中查找,并在该类引用上调用虚拟TComponent.Create
。这会调用实际类的虚构造函数,最终会得到该类型组件的实例,并开始填充其属性。