12

更新:用一个更简单的例子解决了这个问题,最初接受的答案没有回答

给定以下类及其祖先:

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;

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

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

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

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

注意:通常拥有后代构造函数的简单行为会隐藏祖先:

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

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

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

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

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

认为:

  • 我想用祖先重载我的构造函数,
  • 当我真的想用兄弟姐妹重载它时

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

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


尝试回答(失败)

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

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); reintroduce; overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;

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


原始问题

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

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)

注意:这是一个假设的例子。与现实世界一样,在不破坏现有代码的情况下无法更改祖先对象。

现在,当有人使用时,TiPhone我什至不希望他们能够从以下位置看到构造函数TEniac

iphone := TiPhone.Create(powerCord);

更糟糕的是:如果他们调用那个构造函数,他们完全想念我的构造函数,以及在这两者之间完成的所有事情。调用错误的构造函数很容易,它们都在 IDE 代码完成中可见,并将编译:

TiPhone.Create;

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

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

TCellPhone.Create(PowerCord: TPowercord)
begin
   raise Exception.Create('Don''t use.');
end;

但是开发人员不会意识到他们调用了错误的构造函数,直到客户有一天发现错误并对我们处以数十亿美元的罚款。事实上,我试图找到我调用错误构造函数的所有地方 - 但我不知道如何让 Delphi 告诉我!

4

6 回答 6

8

如果我没记错的话,那么reintroduce应该对虚拟方法有所帮助。

reintroduce指令禁止编译器警告关于隐藏先前声明的虚拟方法。当您想用新方法隐藏继承的虚拟方法时,请使用 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;
于 2010-10-06T15:52:44.497 回答
7

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

type
  TComputerClass = class of TComputer;

var
  CellPhoneClass: TComputerClass = TCellPhone;
  CellPhone : TCellPhone;
begin
  CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;

您在任何派生类的代码中所做的任何事情都无法阻止任何人调用 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;

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

于 2010-11-02T05:00:43.097 回答
5

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

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

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

于 2010-10-06T18:43:15.603 回答
4

你不能隐藏父类的构造函数,除非它被声明为虚拟的或动态的。但是,您可以阻止它从子类中调用。考虑你的例子:

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;

TComputer.Create将始终可见TCellPhone。您可以TComputer.Create通过声明TCellPhone.Create具有相同签名的 a 来防止被无意调用。

TCellPhone = class(TComputer)
public
   constructor Create(Teapot: string='');
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

那么只要你inherited的体内没有call toTCellPhone.Create(Teapot: string='')就可以防止TComputer.Create被调用TCellPhone及其后代。以下:

TCellphone.Create;
TCellphone.Create('MyPhone');

将解决 TCellPhone 的实现。

此外:

TiPhone = class(TCellPhone)
    constructor Create;
end;

constructor TiPhone.Create;
begin
  inherited;
end;

将调用TCellPhone.Create而不是TComputer.Create.

于 2010-10-08T04:39:52.467 回答
1

您想重新引入构造函数:

TiPhone = class(TCellPhone)
    constructor Create(sim: TSimChip); reintroduce;

请参阅TComponent.CreateDelphi 源代码中的真实示例。

于 2010-10-06T15:53:33.267 回答
1

我知道这是 5 年前的话题,但它仍然可以帮助某人。隐藏祖先构造函数的唯一方法是将两个 Create 方法之一重命名为其他方法,并删除重载指令的需要。这看起来很奇怪,但这是唯一的方法。至少在旧版本的 Delphi 中是这样。我不知道现在在 XE xxx 版本中是否可能。

于 2015-11-30T13:49:45.420 回答