Delphi: как добавить другой конструктор к потомку?
обновление: пример, который у меня изначально был, был довольно сложным. Вот простой пример 8 строк, который объясняет все в одном блоке кода. Следующее не компилируется предупреждает:
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); virtual;
end;
Примечание: этот вопрос является частью 3 в моей текущей серии вопросов о тонкостях конструкторов в 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"
Я, конечно, не mean чтобы скрыть предка создать - я хочу оба.
как добавить конструктор (с разные signature) для класса потомков, сохраняя при этом конструктор ancestor, чтобы его можно было использовать?
4 ответов
моя немедленная реакция-использовать overload
ключевое слово, например:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
редактировать: спасибо Яну за редактирование, которое делает ответ из моего ответа. Я хотел бы думать, что я получил его за храбрость, поэтому я собираюсь внести более полный пример:
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
вы можете создать два новых перегруженных конструкторов, например:
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;
другая возможность-написать новый конструктор со значениями параметров по умолчанию, где часть подписи с параметрами не по умолчанию соответствует исходному конструктору в базовом классе:
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;
также помните, что конструкторы не должны называться 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;
теперь будут доступны оба конструктора.