使用虚拟构造函数重置为初始状态
我对 Delphi 中可用的虚拟构造函数没有任何经验。我考虑在类层次结构中使用虚拟构造函数将实例重置为初始状态,如下所示:
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;
i do not have any experience with virtual constructors which are available in Delphi. I consider to use virtual ctors in a class hierachy to reset the instance to an initial state like this:
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.
Are there any problems/pitfalls in this design? The simple example works like a charm but i feel a little bit uneasy calling constructors (which do not construct anything) like this.
Edit:
I decided to move the initialization to a method in protected area with a meaningful name, what makes me feel better ;-)
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;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
很少有类被编写为支持使用构造函数作为重新初始化程序。他们通常假设任何动态分配的内存都尚未分配。如果您可以控制正在使用的所有类,那么请继续小心地使用构造函数作为重新初始化程序。
即使你可以控制,我仍然建议不要这样做。 这不是 Delphi 惯用的;其他阅读你的代码的人(也许甚至是你,几周或几个月后)都会对你对构造函数的非标准使用感到困惑,至少在一开始是这样。不值得这么麻烦。如果调用
Bar
函数应该释放A
对象的所有权并创建一个新实例,则编写具有明确含义的名称的函数。Very few classes are written to support the use of constructors as re-initializers. They usually assume that any dynamically allocated memory has not already been allocated. If you're in control of all the classes you're using, then go ahead and carefully use constructors as re-initializers.
Even if you're in control, I'd still advise against it. It's not idiomatic Delphi; anyone else reading your code (perhaps even you, a few weeks or months from now) will be confused — at least at first — by your non-standard use of constructors. It's not worth the trouble. If calling the
Bar
function is supposed to release ownership of theA
object and create a new instance, then write functions with names that make that clear.Rob 的说法是对的,这是看起来非常奇怪的代码,可能会让人们感到困惑,将代码移至初始化例程是一个好主意。如果您想知道,虚拟构造函数的主要目的是完全不同的:更轻松地支持“工厂”风格的对象创建。
一些外部源提供了一些可以识别基类的任何后代的数据,工厂使用类引用并调用基类中定义的虚拟构造函数。这样,您最终会得到后代类的实例,而无需将后代类的知识硬编码到工厂代码中。
如果这听起来有点奇怪,请查看 DFM 文件。它有一个从 TComponent 派生的表单对象列表,以及它们的已发布属性。当表单读取代码遇到
object
语句时,它会读取类名,在将类名映射到类引用的表中查找它,然后调用虚拟TComponent.Create 该类引用。这会调用实际类的虚拟构造函数,最终得到该类型组件的实例,并开始填充其属性。
Rob's right about this being really weird-looking code that's likely to confuse people, and moving your code to an initialization routine is a good idea. In case you were wondering, the main purpose of virtual constructors is for something completely different: to more easily support "factory" style object creation.
Some outside source provides some data that can identify any descendant of a base class, and the factory uses a class reference and calls a virtual constructor defined in the base class on it. That way you end up with an instance of the descendant class without having to hard-code knowledge of the descendant class into the factory code.
If this sounds a bit strange, take a look at a DFM file. It's got a list of form objects that descend from TComponent, with their published properties. When the form reading code comes across an
object
statement, it reads the class name, looks it up in a table that maps class names to class references, and calls the virtualTComponent.Create
on that class reference. This calls the virtual constructor for the actual class, and it ends up with an instance of that type of component, and starts to fill in its properties.