Используйте виртуальный конструктор для сброса в исходное состояние - PullRequest
3 голосов
/ 07 декабря 2010

У меня нет опыта работы с виртуальными конструкторами, доступными в Delphi.Я рекомендую использовать виртуальные ctors в иерархии классов для сброса экземпляра в начальное состояние, например:

A = class
end;
B = class(A)
end;
C = class(B)
end;

FooA = class
    a_ : A;
    constructor Create(inst : A); overload;
    constructor Create; overload; virtual; abstract;
    destructor Destroy; override;
    function Bar : A;
end;

FooB = class(FooA)
    b_ : B;
    constructor Create; override;
    constructor Create(inst : B); overload;
end;

FooC = class(FooB)
// ...
end;

{ FooA }
constructor FooA.Create(inst: A);
begin
    inherited Create;
    a_ := inst;
end;

destructor FooA.Destroy;
begin
    FreeAndNil(a_);
    inherited;
end;

function FooA.Bar : A;
begin
    Result := a_;
    a_ := nil;

    // here comes the magic
    Self.Create;
end;

{ FooB }
constructor FooB.Create;
begin
    b_ := B.Create;
    inherited Create(b_);
end;

constructor FooB.Create(inst: B);
begin
    inherited Create(inst);
    b_ := inst;
end;
{ FooC } // ...

var
    fc : FooA;
    baz : A;
begin
    fc := FooC.Create;
    baz := fc.Bar;
    WriteLn(baz.ClassName);
    FreeAndNil(baz);
    FreeAndNil(fc);
    ReadLn;
end.

Есть ли какие-либо проблемы / подводные камни в этом проекте?Простой пример работает как шарм, но я чувствую себя немного неловко, вызывая конструкторы (которые ничего не конструируют), как это.

Edit:

Я решил перенести инициализацию в метод вохраняемая территория со значимым названием, что заставляет меня чувствовать себя лучше; -)

FooA = class
strict private
    a_ : A;
strict protected
    procedure SetInst; overload; virtual; abstract;
    procedure SetInst(i : A); overload;
public
    constructor Create;
    destructor Destroy; override;
    function Foo : A;
end;

Ответы [ 2 ]

3 голосов
/ 07 декабря 2010

Очень мало классов написано для поддержки использования конструкторов в качестве реинициализаторов. Обычно они предполагают, что любая динамически распределенная память уже выделена , а не . Если вы контролируете все используемые вами классы, тогда продолжайте и осторожно используйте конструкторы в качестве реинициализаторов.

Даже если вы контролируете ситуацию, я все равно советую против этого. Это не идиоматический Delphi ; любой, кто читает ваш код (возможно, даже вы, через несколько недель или месяцев), будет смущен - по крайней мере, на первый взгляд - вашим нестандартным использованием конструкторов. Это не стоит хлопот. Если при вызове функции Bar предполагается освобождение владельца объекта A и создание нового экземпляра, напишите функции с именами, которые проясняют это.

1 голос
/ 07 декабря 2010

Роб прав в том, что это действительно странно выглядящий код, который может сбить людей с толку, и перевод вашего кода в процедуру инициализации - хорошая идея.Если вам было интересно, основная цель виртуальных конструкторов - это нечто совершенно иное: упростить поддержку создания объектов в «фабричном» стиле.

Некоторые внешние источники предоставляют данные, которые могут идентифицировать любого потомка базового классаи фабрика использует ссылку на класс и вызывает для нее виртуальный конструктор, определенный в базовом классе.Таким образом, вы получите экземпляр класса-потомка без необходимости жесткого кодирования знания класса-потомка в заводской код.

Если это звучит немного странно, взгляните на файл DFM.У него есть список объектов формы, которые происходят от TComponent, с их опубликованными свойствами.Когда код чтения формы встречает оператор object, он читает имя класса, ищет его в таблице, которая отображает имена классов на ссылки на классы, и вызывает виртуальную TComponent.Create для этой ссылки на класс.Это вызывает виртуальный конструктор для фактического класса, и он заканчивается экземпляром компонента этого типа и начинает заполнять его свойства.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...