德尔福(Delphi):如何在后代中重载父构造函数,但如何将其隐藏在其他不同的后代中

时间:2018-07-24 11:03:40

标签: delphi

我读过answer的有关构造函数及其指令(重新引入,重载,虚拟,覆盖等)的信息,但我达不到想要的目标。检查以下伪代码(我的意思是代码还没有任何指令):

TBaseClass = class
  constructor Create;
end;

TStringStuff = class (TBaseClass)
  constructor Create(str: string);
end;

TNumberStuff = class (TBaseClass)
  constructor Create(num: integer);
  constructor Create(num: decimal);
end;

我希望可以使用其自己的构造函数和父构造函数创建一个TStringStuff对象:

var
  StrStuff: TStringStuff;
begin
  StrStuff:=TStringStuff.Create();
  //or 
  StrStuff:=TStringStuff.Create('bla');
end;

但是我也希望只能使用它自己的构造函数来创建TNumberStuff对象,即,如果有人使用我的库,他将无法创建没有参数的TNumberStuff:

var
  NumStuff: TNumberStuff ;
begin
  NumStuff:=TNumberStuff.Create(10);
  //or 
  NumStuff:=TNumberStuff.Create(10.5);
  // but NOT: NumStuff:=TNumberStuff.Create(); 
end;

那么如何使用指令来实现我的目标呢?

(我正在使用Delphi 10.2 Tokyo)

2 个答案:

答案 0 :(得分:1)

由于多种原因,您无法完全实现所需的目标,但这离您能达到的目标很近。

如果您喜欢使用隐藏的构造函数,并且仅在该单元中可见,我已经引入了'superbase'类。现在,TBaseClass的唯一功能是“公开”构造函数,以便您可以创建TBaseClass的实例。

bash script.sh

在另一个单元中,如果您放置了这样的测试程序

unit Test1;

interface

type
  TBaseBaseClass = class
    // This does all the work of TBaseClass, but hides the contructor
  private
    constructor Create; reintroduce; // this can only be accessed within this unit
  end;

  TBaseClass = class(TBaseBaseClass)
    // a creatable class. No actual work is done here. It's only purpose is to
    // 'expose' the base constructor
  public
    constructor Create; reintroduce;
  end;

  TStringStuff = class( TBaseBaseClass )
  public
    constructor Create; reintroduce; overload;
    constructor Create( str : string ); reintroduce; overload;
  end;

  TNumStuff = class( TBaseBaseClass )
  public
    constructor Create( num : integer ); reintroduce; overload;
    constructor Create( num : single ); reintroduce; overload;
  end;

implementation

{ TStringStuff }

constructor TStringStuff.Create(str: string);
begin
  inherited Create;
  // ...
  // other stuff
end;

constructor TStringStuff.Create;
begin
  inherited Create;
  // does no extra work! 'exposes' TBaseBaseClass constructor
  // but required because of rules of polymorphism
end;

{ TBaseBaseClass }

constructor TBaseBaseClass.Create;
begin
  inherited Create;
  // ...
  // other stuff - does the work originally in TBaseClass
end;

{ TBaseClass }

constructor TBaseClass.Create;
begin
  inherited Create;
  // does no extra work! 'exposes' TBaseBaseClass constructor
end;

{ TNumStuff }

constructor TNumStuff.Create(num: single);
begin
  inherited Create;
  // ...
  // other stuff
end;

constructor TNumStuff.Create(num: integer);
begin
  inherited Create;
  // ...
  // other stuff
end;


end.

您会发现它无法编译。

但是有两个“陷阱”。如果您尝试将此过程放在与原始定义相同的单元中,它将进行编译。这是因为TBaseBase构造函数在该单元中可见。

第二个陷阱与以下事实有关:隐藏的构造函数是无参数的,并且TObject有一个公共的无参数构造函数,所有对象都从该构造函数派生。因此,如果尝试使用不带参数的构造函数创建TBaseBaseClass的实例,它将进行编译。它只是不会使用您可能期望的构造函数。它将使用TObject构造函数。

最后,我建议不要试图束缚其他程序员。一定要引导他们朝正确的方向前进,但不要试图阻止他们做自己想做的事情。考虑到这一点,我不会这样做。我将使TBaseBase构造函数受保护,而不是私有的。我只是显示这个来回答你的问题。

答案 1 :(得分:1)

这就是我要如何处理您的问题

首先,我将BaseClass构造函数设置为虚拟的,从而允许在子类中重写它。

TBaseClass = class
  constructor Create; virtual;
end;

然后在TStringStuff类中,我将更改现有的构造函数,以便其字符串参数实际上是可选参数。这将允许您在传递或不传递字符串参数的情况下调用此构造函数。因此,现在您需要做的只是在没有参数传递给构造函数的情况下在继承的帮助下调用父构造函数,或者在将字符串参数传递给构造函数之前做必要的工作。

TStringStuff = class (TBaseClass)
  constructor Create(str: string = ''); override;
end;

在TNumberStuff类中,您只需重新引入重载的构造函数即可管理可以传递给构造函数的多个可能的输入参数类型。

TNumberStuff = class (TBaseClass)
  constructor Create(num: integer); reintroduce; overload;
  constructor Create(num: decimal); reintorduce; overload;
end;