Delphi:重写的虚拟构造函数后代未被重载调用

发布于 2024-09-26 08:38:35 字数 2311 浏览 9 评论 0原文

这是我关于 Delphi 构造函数的一系列问题中的另一个问题。

我有一个具有虚拟构造函数的基类:

TComputer = class(TObject)
public
    constructor Create(Teapot: Integer); virtual;
end;

构造函数在有人需要调用时

var
   computerClass: class of TComputer;
   computer: TComputer;
begin     
   computer := computerClass.Create(nTeapot);

是虚拟的构造函数在后代中被重写

TCellPhone = class(TComputer) 
public
   constructor Create(Teapot: Integer); override;
end;

TiPhone = class(TCellPhone ) 
public
   constructor Create(Teapot: Integer); override;
end;

其中TCellPhoneTiPhone 后代每个都有机会进行自己的初始化(为了便于阅读而未包括成员)。

但现在我向某个祖先添加了一个重载的构造函数:

TCellPhone = class(TComputer) 
public
   constructor Create(Teapot: Integer); override; overload;
   constructor Create(Teapot: Integer; Handle: string); overload;
end;

TCellPhone 中的备用构造函数调用另一个虚拟构造函数,因此它总是获得正确的重写行为:

constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
   TCellPhone.Create(Teapot); //call sibling virtual constructor

   FHandle := Handle;
end;

问题是后代、重写的构造函数永远不会叫。实际的堆栈跟踪调用链是:

phone := TiPhone.Create(37, 'spout')
   constructor TCellPhone.Create(Teapot: Integer; Handle: string)
      constructor TCellPhone.Create(Teapot: Integer)
         constructor TComputer.Create(Teapot: Integer)
            TObject.Create

TCellPhone.Create(int) 的同级调用是虚拟的,应该调用 TiPhone 中的后代、重写方法:

phone := TiPhone.Create(37, 'spout')
   constructor TCellPhone.Create(Teapot: Integer; Handle: string)
      constructor TiPhone.Create(Teapot: Integer)
         constructor TCellPhone.Create(Teapot: Integer)
            constructor TComputer.Create(Teapot: Integer)
               TObject.Create

因此,Delphi 尝试使用同级虚拟构造函数似乎无法按预期工作。

那么一个构造函数使用另一个构造函数是一个坏主意吗?设计意图是重载构造函数中的代码是彼此的复制粘贴版本吗?

我注意到在 .NET 中,一些构造函数相互链接:

public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) {}

public Bitmap(int width, int height, PixelFormat format) {...}

这似乎只是一个问题,如果:

  • 构造函数是虚拟的
  • 您重载了构造函数

规则是不能让一个构造函数重载另一个构造函数吗?

Yet another in my series of questions regarding constructors in Delphi.

i have a base class that has has the virtual constructor:

TComputer = class(TObject)
public
    constructor Create(Teapot: Integer); virtual;
end;

The constructor is virtual for the times that someone needs to call

var
   computerClass: class of TComputer;
   computer: TComputer;
begin     
   computer := computerClass.Create(nTeapot);

The constructor is overridden in descendants:

TCellPhone = class(TComputer) 
public
   constructor Create(Teapot: Integer); override;
end;

TiPhone = class(TCellPhone ) 
public
   constructor Create(Teapot: Integer); override;
end;

Where TCellPhone and TiPhone descendants each have their opportunity to do their own initialization (of members not included for readability).

But now i add an overloaded constructor to some ancestor:

TCellPhone = class(TComputer) 
public
   constructor Create(Teapot: Integer); override; overload;
   constructor Create(Teapot: Integer; Handle: string); overload;
end;

The alternate constructor in TCellPhone calls the other virtual constructor, so it always gets the proper overridden behaviour:

constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
   TCellPhone.Create(Teapot); //call sibling virtual constructor

   FHandle := Handle;
end;

The problem is that the descendant, overridden, constructor is never called. The actual stack trace chain of calls is:

phone := TiPhone.Create(37, 'spout')
   constructor TCellPhone.Create(Teapot: Integer; Handle: string)
      constructor TCellPhone.Create(Teapot: Integer)
         constructor TComputer.Create(Teapot: Integer)
            TObject.Create

The sibling call to TCellPhone.Create(int), which is virtual, should have called the descendant, overridden, method in TiPhone:

phone := TiPhone.Create(37, 'spout')
   constructor TCellPhone.Create(Teapot: Integer; Handle: string)
      constructor TiPhone.Create(Teapot: Integer)
         constructor TCellPhone.Create(Teapot: Integer)
            constructor TComputer.Create(Teapot: Integer)
               TObject.Create

So it seems that attempts to use a sibling virtual constructor is Delphi do not work as expected.

Is it then a bad idea for one constructor to use another? Is the design intention that code in overloaded constructors be copy-paste versions of each other?

i notice in .NET that some constructors chain to each other:

public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) {}

public Bitmap(int width, int height, PixelFormat format) {...}

This only seems to be a problem if:

  • a constructor is virtual
  • you overload the constructors

Is the rule that you cannot have one constructor overload another?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

以酷 2024-10-03 08:38:35

呃..

constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
   TCellPhone.Create(Teapot); //call sibling virtual constructor

   FHandle := Handle;
end;

那应该是:

constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
   Create(Teapot); //call sibling virtual constructor

   FHandle := Handle;
end;

您只是创建了一个新的 TCellphone 实例,而不是调用其他 Create 方法。

Errr..

constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
   TCellPhone.Create(Teapot); //call sibling virtual constructor

   FHandle := Handle;
end;

That should be:

constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
   Create(Teapot); //call sibling virtual constructor

   FHandle := Handle;
end;

You were just creating a new TCellphone instance and not calling the other Create method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文