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 构造函数的微妙之处的系列问题的第 3 部分

原始问题

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

让我们举一个假设的例子(即我在 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 抱怨我的新构造函数隐藏了旧构造函数。

方法“创建”隐藏基本类型“TXMLStream”的虚拟方法

我当然不是有意隐藏祖先的创造——我想要两者。

如何将构造函数(具有不同的签名)添加到后代类,同时保留祖先构造函数以便它仍然可以使用?

4

4 回答 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
于 2010-10-06T20:30:52.857 回答
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;
于 2010-10-06T20:48:12.860 回答
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;
于 2010-10-06T20:54:55.623 回答
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;

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

于 2010-10-06T21:14:37.440 回答