Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.224.0.25] |
|
Сообщ.
#1
,
|
|
|
Плюсовый шаблон auto_ptr одна из тех фишек, которых на мой скромный взгляд очень не хватает в Delphi.
В некоторой степени приближения, я попытался изобразить его аналог с помощью средств последних версий языка в Delphi. Получилось следующее (интерфейс по сути общий, но с реализаций пока только для классов, но под другие ресурсы добавить поддержку не составит трудов): unit AutoPtr; interface type auto_ptr<TResource> = interface function is_empty(): Boolean; function get(): TResource; function release(): TResource; procedure reset(ToAnother: TResource); property Empty: Boolean read is_empty; property Obj: TResource read get; end; TObjectContainer<TClass: class> = class(TInterfacedObject, auto_ptr<TClass>) strict private FObject: TClass; public constructor Create(Obj: TClass); destructor Destroy(); override; function is_empty(): Boolean; inline; function get(): TClass; inline; function release(): TClass; inline; procedure reset(ToAnother: TClass); end; auto_class_ptr<TClass: class> = record Container: TObjectContainer<TClass>; class operator Explicit(Obj: TClass): auto_class_ptr<TClass>; inline; class operator Implicit(Tmp: auto_class_ptr<TClass>): auto_ptr<TClass>; inline; end; implementation { TObjContainer<TClass> } constructor TObjectContainer<TClass>.Create(Obj: TClass); begin inherited Create(); FObject := Obj; end; destructor TObjectContainer<TClass>.Destroy(); begin FObject.Free(); inherited Destroy(); end; function TObjectContainer<TClass>.is_empty(): Boolean; begin Result := (FObject = nil); end; function TObjectContainer<TClass>.get(): TClass; begin Result := FObject; end; function TObjectContainer<TClass>.release(): TClass; begin Result := FObject; FObject := nil; end; procedure TObjectContainer<TClass>.reset(ToAnother: TClass); var Old: TClass; begin Old := FObject; if (ToAnother <> Old) then begin FObject := ToAnother; Old.Free(); end; end; { auto_class_ptr<TClass> } class operator auto_class_ptr<TClass>.Explicit(Obj: TClass): auto_class_ptr<TClass>; var Tmp: auto_class_ptr<TClass>; begin Tmp.Container := TObjectContainer<TClass>.Create(Obj); Result := Tmp; end; class operator auto_class_ptr<TClass>.Implicit(Tmp: auto_class_ptr<TClass>): auto_ptr<TClass>; begin Result := Tmp.Container; end; end. Тип-запись auto_class_ptr не обязателен на самом деле, а служит лишь для лаконичности и удобности кодирования и чтения на мой вкус. Можно обойтись без него. Итак, зачем это нужно. Механизм призван заменить конструкции в коде вида: procedure TForm1.ButtonClick(Sender: TObject); var Strings: TStrings; begin Strings := TStringList.Create(); try Strings.LoadFromFile('.\TestFile.txt'); if (Strings.Count > 0) then Self.Caption := Format('Got %d line(s) in file', [Strings.Count]) else Self.Caption := 'File is empty'; finally Strings.Free(); end; end; На укороченные, но также гарантирующие освобождение ресурсов: procedure TForm1.ButtonClick(Sender: TObject); var Strings: auto_ptr<TStrings>; begin Strings := auto_class_ptr<TStrings>(TStringList.Create()); Strings.Obj.LoadFromFile('.\TestFile.txt'); if (Strings.Obj.Count > 0) then Self.Caption := Format('Got %d line(s) in file', [Strings.Obj.Count]) else Self.Caption := 'File is empty'; end; Без использования auto_class_ptr это выглядит так: procedure TForm1.Button4Click(Sender: TObject); var Strings: auto_ptr<TStrings>; begin Strings := TObjectContainer<TStrings>.Create(TStringList.Create());//просто непосредственное создание объекта-оболочки Strings.Obj.LoadFromFile('.\TestFile.txt'); if (Strings.Obj.Count > 0) then Self.Caption := Format('Got %d line(s) in file', [Strings.Obj.Count]) else Self.Caption := 'File is empty'; end; Каков выигрыш: в представленном простейшем случае экономия 4 строки по вертикали и 1 уровень отступов по горизонтали на основной рабочий код. И это в простейшем случае, а чем сложнее (вложенные или просто последовательные получения ресурсов), тем больше. А за краткость методов я борюсь и для меня это важно. Насчет выигрыша по горизонтали Да, выигрыш по горизонтали несколько сомнителен, т.к. требуется дополнительные символы ".Obj", но Кроме замены указанных конструкций кода, auto_ptr применим и для хранения агрегированных объектов внутри объектов-контейнеров дабы отказаться от ручного освобождения в деструкторе, что в свою очередь в некоторых случаях отказаться и от самого деструктора. З.Ы. Не претендую пока на абсолютную правильность и корректность такого способа, отсутствие подводных камней и прочего, а лишь выношу на обсуждение. Для отыскания истины) |
Сообщ.
#2
,
|
|
|
Сообщ.
#3
,
|
|
|
Я на работе набросал логичноподобный код. но я не насыщал интерфейс логикой. мне показалось это лишним. он служит только для уничтожения класса.
и всю внутреннюю кухню всунул в record (объявления типа и интерфейса) пс. хотя насчет этого в 2009 глюк, но у меня не стояла задача делать совместимость. ну и я сделал авто создание. гдето так: type TRecord<T:class, constructor> = record private inf: IInterface; function GetValue: T; public property Value: T read GetValue write SetValue; end; { TRecord<T> } function TRecord<T>.GetValue: T; begin if not Assigned(inf) then inf := TContainer.Create(T.Create); Result := inf.GetObj; end; и тогда можно будет написать var Strings: auto_ptr<TStringList>; begin Strings.Obj.LoadFromFile('.\TestFile.txt'); // объект создавать не надо он сам будет создан if (Strings.Obj.Count > 0) then |