Delphi:如何隐藏祖先构造函数?

Ian*_*oyd 11 delphi constructor delphi-5 constructor-chaining

更新:用一个更简单的例子来解决问题,原来接受的答案没有回答

鉴于以下类及其祖先:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Run Code Online (Sandbox Code Playgroud)

现在TCellPhone有3个构造函数可见:

  • 杯子:整数
  • 杯子:整数; 茶壶:串
  • 茶壶:string =''

我该怎么做才能TCellPhone使祖先构造函数(Teapot: string = '')不可见,只留下声明的构造函数:

  • 杯子:整数
  • 杯子:整数; 茶壶:串

:通常简单的行为一个后代的构造函数隐藏了祖先:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); virtual;
end;
Run Code Online (Sandbox Code Playgroud)
  • 杯子:整数

如果你保留祖先构造函数和后代,你可以将后代标记为overload:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
end;
Run Code Online (Sandbox Code Playgroud)
  • 杯子:整数
  • 茶壶:string =''

在这个问题的示例代码中,Delphi误解了我的overload关键字:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Run Code Online (Sandbox Code Playgroud)

认为:

  • 我想用祖先重载我的构造函数,
  • 什么时候我真的想和兄弟姐妹一起超负荷

我如何隐藏祖先构造函数?

注意:可能无法使用当前定义的Delphi语言隐藏祖先,非虚拟构造函数."不可能"是一个有效的答案.


尝试答案(失败)

试着标记后代构造函数reintroduce(回到我的模式,随机添加关键字,直到它工作):

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); reintroduce; overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
Run Code Online (Sandbox Code Playgroud)

但这不起作用,所有三个构造函数仍然可见.:(


原始问题

我有一个对象,它来自一个有构造函数不想看到的类:

TEniac = class(TObject)
   constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create

TComputer = class(TEniac) ...
   constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create(nil)

TCellPhone = class(TComputer)
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)

TiPhone = class(TCellPhone)
   constructor Create(sim: TSimChip); //calls inherited Create(sim, 0)
Run Code Online (Sandbox Code Playgroud)

注意:这是一个假设的例子.与现实世界一样,如果不破坏现有代码,则无法更改祖先对象.

现在当有人使用TiPhone我不希望他们甚至能够从以下方面看到构造函数时TEniac:

iphone := TiPhone.Create(powerCord);
Run Code Online (Sandbox Code Playgroud)

更糟糕的是:如果他们称之为构造函数,他们会完全错过我的构造函数,并且完成所有工作.调用错误的构造函数非常容易,所有这些都在IDE代码完成中可见,并将编译:

TiPhone.Create;
Run Code Online (Sandbox Code Playgroud)

他们得到一个完全无效的对象.

我可以更改TCellPhone为在这些构造函数中抛出异常:

TCellPhone.Create(PowerCord: TPowercord)
begin
   raise Exception.Create('Don''t use.');
end;
Run Code Online (Sandbox Code Playgroud)

但是开发人员不会意识到他们正在调用错误的构造函数,直到客户有一天发现错误并罚款我们的数百美元.事实上,我试图找到我称之为错误的构造函数 - 但我无法弄清楚如何让Delphi告诉我!

spl*_*ash 6

如果我没记错的话,reintroduce应该帮助虚拟方法.

重新引入指令禁止有关隐藏先前声明的虚拟方法的编译器警告.如果要使用新方法隐藏继承的虚拟方法,请使用重新引入.

要回答你更新的问题 - 我认为在直接派生类中隐藏非虚拟构造函数是不可能的,但我尝试了以下成功:

TComputer = class(TObject)
public
  constructor Create(Teapot: string='');
end;

TIndermediateComputer = class(TComputer)
protected
  // hide the constructor
  constructor Create;
end;

TCellPhone = class(TIndermediateComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Run Code Online (Sandbox Code Playgroud)

  • @Ian.哦,亲爱的,大脑似乎无所事事.好的,所以没有编译时消息,但是当重新实现构造函数时,您可以抛出异常或使用默认参数调用正确的构造函数... (2认同)

Tho*_*ler 6

为了在Delphi中创建派生类,不可能在祖先中引入构造函数是不可能的,因为你总是可以这样做:

type
  TComputerClass = class of TComputer;

var
  CellPhoneClass: TComputerClass = TCellPhone;
  CellPhone : TCellPhone;
begin
  CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;
Run Code Online (Sandbox Code Playgroud)

你在任何派生类的代码中所做的任何事情都无法阻止任何人调用TComputer.Create构造函数来创建派生类的实例.

你能做的最好的事情是:

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

TCellPhone = class(TComputer)
public
   constructor Create(Teapot: string=''); overload; override;
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,上面的代码至少会调用TCellPhone.Create(Teapot: string='')而不是TComputer.Create(Teapot: string='')


Mar*_*ema 5

不要只在被覆盖的无效构造函数中引发“不要使用”异常,而是考虑在它们变得无效的类中将它们标记为弃用。当这些无效的构造函数被错误地使用时,这应该会产生很好的编译器警告。

TCellPhone = class(TComputer)
   constructor Create(PowerCord: TPowerCord=nil); deprecated;
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
Run Code Online (Sandbox Code Playgroud)

此外,根据需要使用覆盖或重新引入。