Delphi:如何为派生类添加不同的构造函数?

8
更新:我最初提供的例子有点复杂。这里是一个简单的8行示例,用一个代码块解释了所有内容。以下内容不会编译但会给出警告:
TComputer = class(TObject)
public
    constructor Create(Cup: Integer); virtual;
end;

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

注意:这个问题是我关于Delphi构造函数细节的系列问题中的第三部分。

原始问题

如何为现有类添加构造函数?

让我们举一个假设的例子(即我在SO编辑器中输入的,可能无法编译):

TXHTMLStream = class(TXMLStream)
public
   ...
end;

进一步假设正常使用 TXHTMLStream 需要在使用之前执行大量重复的代码:
var
   xs: TXHTMLStream;
begin
   xs := TXHTMLStream.Create(filename);
   xs.Encoding := UTF32;
   xs.XmlVersion := 1.1;
   xs.DocType := 'strict';
   xs.PreserveWhitespace := 'true';
   ...

   xs.Save(xhtmlDocument);

假设我想创建一个构造函数,它可以简化所有繁琐的设置代码:
TXHTMLStream = class(TXMLStream)
public
    constructor Create(filename: string; Encoding: TEncoding); virtual;
end;

constructor TXHTMLStream.Create(filename: string; Encoding: TEncoding);
begin
   inherited Create(filename);
   xs.Encoding := Encoding;
   xs.XmlVersion := 1.1;
   xs.DocType := 'strict';
   xs.PreserveWhitespace := True;
   ...
end;

这简化了对象的使用方式:
var
   xs: TXHTMLStream;
begin
   xs := TXHTMLStream.Create(filename, UTF32);
   xs.Save(xhtmlDocument);

现在Delphi抱怨我的新构造函数隐藏了旧的构造函数。

方法“Create”隐藏了基类型“TXMLStream”的虚方法

我肯定不是想隐藏祖先的create - 我希望两个都存在。

如何在一个派生类中添加一个构造函数(具有不同的签名),同时保留祖先构造函数,以便仍然可以使用它?

4个回答

8

我的直接反应是使用 overload 关键字,例如:

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

编辑:感谢Ian的编辑,让我的回答更加完整。我想说我是因为勇气而得到这个机会,所以我将贡献一个更全面的例子:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type

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

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

{ TComputer }

constructor TComputer.Create(Cup: Integer);
begin
  writeln('constructed computer: cup = ', Cup);
end;

{ TCellPhone }

constructor TCellPhone.Create(Cup: Integer; Teapot: string);
begin
  inherited Create(Cup);
  writeln('constructed cellphone: Teapot = ', Teapot);
end;

var
  C1, C2, C3: TComputer;

begin
  C1 := TComputer.Create(1);
  Writeln;
  C2 := TCellPhone.Create(2);
  Writeln;
  C3 := TCellPhone.Create(3, 'kettle');
  Readln;
end.

结果如下:

constructed computer: cup = 1

constructed computer: cup = 2

constructed computer: cup = 3
constructed cellphone: Teapot = kettle

应该也要“重新引入” - 这将抑制提示。 - Gerry Coll
@Gerry,就是这样。这让我很烦恼,因为现在重新引入被用来隐藏祖先方法 - 而5小时前我被告知它会隐藏祖先方法。(https://dev59.com/em865IYBdhLWcg3wUs7z) 我同时有四个问题,而且一直在打转。 - Ian Boyd
1
@Ian - reintroduce 只是抑制警告,它并没有做更多的事情,也不会隐藏任何东西。真正隐藏基类方法的是使用相同名称但未覆盖它的方法。 - Sertac Akyuz
@Ian,“reintroduce” 不会“显示”祖先方法。您引用的警告显然是错误的,因为该方法并没有被隐藏。Muhammad 的示例输出证明该方法可见且可调用。 - Rob Kennedy
5
只是为了强调之前的评论,reintroduce 的作用等同于本地形式的“禁止警告 W1010”。它只有这个作用。 - Muhammad Alkarouri
显示剩余4条评论

3
您可以创建两个新的重载构造函数,例如:
type
  TXmlStream = class
  private
    FFileName: string;
  public
    constructor Create(const AFileName: string); virtual;
  end;

  TXhtmlStream = class(TXmlStream)
  private
    FEncoding: TEncoding;
  public
    constructor Create(const AFileName: string); overload; override;
    constructor Create(const AFileName: string; AEncoding: TEncoding); overload; virtual;
  end;

constructor TXmlStream.Create(const AFileName: string);
begin
  inherited Create;
  FFileName := AFileName;
end;

constructor TXhtmlStream.Create(const AFileName: string);
begin
  inherited Create(AFileName);
end;

constructor TXhtmlStream.Create(const AFileName: string; AEncoding: TEncoding);
begin
  inherited Create(AFileName);
  FEncoding := AEncoding;
end;

2
另一个可能的方法是编写一个带有默认参数值的新构造函数,其中带有非默认参数的签名部分与基类中的原始构造函数匹配:
type
  TXmlStream = class
  private
    FFileName: string;
  public
    constructor Create(const AFileName: string); virtual;
  end;

  TXhtmlStream = class(TXmlStream)
  private
    FEncoding: TEncoding;
  public
    constructor Create(const AFileName: string; AEncoding: TEncoding = encDefault); reintroduce; virtual;
  end;

constructor TXmlStream.Create(const AFileName: string);
begin
  inherited Create;
  FFileName := AFileName;
end;

constructor TXhtmlStream.Create(const AFileName: string; AEncoding: TEncoding);
begin
  inherited Create(AFileName);
  FEncoding := AEncoding;
end;

2

还要记住,构造函数不一定要叫做Create。早期的Delphi版本没有方法重载,所以你必须使用不同的名称:

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

TCellPhone = class(TComputer) 
private
  FTeapot: string;
public 
    constructor CreateWithTeapot(Cup: Integer; Teapot: string); virtual; 
end; 

...

constructor TCellPhone.CreateWithTeapot(Cup: Integer; Teapot: string); 
begin
  Create(Cup);
  FTeapot := Teapot;
end;

现在两个构造函数都可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接