Delphi: How to hide ancestor constructors?

自古美人都是妖i 提交于 2019-11-27 01:59:24

It's impossible to ever make a constructors introduced in an ancestor inaccessible for the creation of a derived class in Delphi because you can always do this:

type
  TComputerClass = class of TComputer;

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

Nothing you could do in the code of any derived class would ever be able to prevent anyone from calling the TComputer.Create constructor for creating an instance of the derived class.

The best you could do is:

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;

In that case the code above would at least be calling TCellPhone.Create(Teapot: string='') instead of TComputer.Create(Teapot: string='')

If I remember correctly, then reintroduce should help for virtual methods.

The reintroduce directive suppresses compiler warnings about hiding previously declared virtual methods. Use reintroduce when you want to hide an inherited virtual method with a new one.

To answer your updated question - I think it's not possbile to hide a non-virtual constructor with overloading in a directly derived class, but I tried the following successfully:

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;

You can't hide the parent class' constructor unless it was declared virtual or dynamic. You can however prevent it from being called from the child class. Consider your example:

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 will always be visible from TCellPhone. You can prevent TComputer.Create from being inadvertantly called by declaring a TCellPhone.Create with the same signature.

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

Then as long as you don't have a call to inherited in the body of TCellPhone.Create(Teapot: string='') you can prevent TComputer.Create from being called in TCellPhone and its descendants. The following:

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

Will resolve to TCellPhone's implementation.

Additionally:

TiPhone = class(TCellPhone)
    constructor Create;
end;

constructor TiPhone.Create;
begin
  inherited;
end;

Will invoke TCellPhone.Create and not TComputer.Create.

Instead of only raising an "Don't use" exception in the overridden invalid constructors, consider marking them deprecated in the class where they become invalid. That should produce nice compiler warnings when these invalid constructors are used erroneosly.

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

In addition, use override or reintroduce as needed.

You want to reintroduce the constructor:

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

See TComponent.Create in the Delphi source code for a real-world example of this.

I know that this is 5 years old topic, but still it may help someone. The only way to hide the ancestor's constructor is to rename one of the two Create methods to something else and remove the need of overload directive. It looks weird but it's the only way. At least in older versions of Delphi. I don't know is it possible now in the XE xxx versions.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!