Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.04.15;
Скачать: CL | DM;

Вниз

Автоматическое уничтожение объектов   Найти похожие ветки 

 
jack128 ©   (2007-03-20 10:51) [0]

вот такая(наверно не новая) мысль.

например такой код.  
 MyObj1 := TMyObj1.Create;
 try
   while <> do
   begin
     MyObj2 := TMyObject2.Create;
     try
       ....
     finally
       MyObj2.Free;
     end;
   end;
 finally
   MyObj1.Free;
 end;

Бросается в глаза туча try-finally-end

Предлагается следать так

type
 IDestroyer = interface
 ["{2EAF5F09-7CAF-4799-8958-4DFB9D9C8ADD}"]
 end;

 TDestroyer = class(TInterfacedObject, IDestroyer)
 strict private
   FObj: TObject;
 public
   constructor Create(AObj: TObject);
   destructor Destroy; override;
 end;

function CreateDestroyerIntf(AObj: TObject): IDestroyer;
begin
 Result := TDestroyer.Create(AObj);
end;

{ TDestroyer }

constructor TDestroyer.Create(AObj: TObject);
begin
 FObj := AObj;
end;

destructor TDestroyer.Destroy;
begin
 FreeAndNil(FObj);
 inherited;
end;


Теперь исходный код можно переписать так:

 MyObj1 := TMyObj1.Create;
 CreateDestroyerIntf(MyObj1);
 while <> do
 begin
   MyObj2 := TMyObject2.Create;
   CreateDestroyerIntf(MyObj2);
   ...
 end;


На лицо сокращение кода. У кого есть какие соображения?


 
Ega23 ©   (2007-03-20 10:57) [1]


> На лицо сокращение кода. У кого есть какие соображения?


Я достаточно мало с интерфейсами работал. Не могу понять, а где, собственно, будет вызван destructor TDestroyer.Destroy?
При выходе из данной процедуры (Ну, из области видимости)?


 
jack128 ©   (2007-03-20 10:57) [2]

jack128 ©   (20.03.07 10:51)
MyObj1 := TMyObj1.Create;
CreateDestroyerIntf(MyObj1);
while <> do
begin
  MyObj2 := TMyObject2.Create;
  CreateDestroyerIntf(MyObj2);
  ...
end;

вместо многоточия естественно могут быть обращения к созданным обдъектам, иначе все это смысла бы не имело.

MyObj1 := TMyObj1.Create;
CreateDestroyerIntf(MyObj1);
while <> do
begin
  MyObj2 := TMyObject2.Create;
  CreateDestroyerIntf(MyObj2);
  MyObj1.DoWork();
  MyObj2.DoWork();
end;


 
Джо ©   (2007-03-20 10:57) [3]

> При выходе из данной процедуры (Ну, из области видимости)
> ?

Да.


 
Сергей М. ©   (2007-03-20 11:00) [4]


> jack128


Не проще ли сделать TMyObj1 и TMyObj2 наследниками TComponent и делать объект класса TMyObj1 владельцем всех объектов класса TMyObj2 ?


 
Джо ©   (2007-03-20 11:04) [5]

> [4] Сергей М. ©   (20.03.07 11:00)
>
> > jack128
>
>
> Не проще ли сделать TMyObj1 и TMyObj1  наследниками TComponent
> и делать объект класса TMyObj1 владельцем всех объектов
> класса TMyObj2 ?

Метод jack128 хорош тем, что не требует изменения классов TMyObj1 TMyObj2.


 
Ega23 ©   (2007-03-20 11:11) [6]


> Джо ©   (20.03.07 10:57) [3]
>
> > При выходе из данной процедуры (Ну, из области видимости)
> > ?
>
> Да.


Т.е., если я правильно понял, _Release всегда неявным образом вызовется при выходе из данной области видимости?
Век живи - век учись...


 
oxffff ©   (2007-03-20 11:13) [7]


> На лицо сокращение кода. У кого есть какие соображения?


В твоем подходе объекты, будут освобождаться при финализации временных переменных IDestroyer. Что не дает возможности отловить исключения.

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end
end;

У тебя все равно не будет контроля над освобождением объектов.

Да и зачем так сложно

IDestroyer вообще не нужен

TDestroyer = class(TInterfacedObject)
strict private
  FObj: TObject;
public
  constructor Create(AObj: TObject);
  destructor Destroy; override;
end;

function CreateDestroyerIntf(AObj: TObject): IInterface
begin
Result := TDestroyer.Create(AObj);
end;

Имхо. Теряется контроль над освобождением


 
SlymRO ©   (2007-03-20 11:16) [8]

jack128 ©   (20.03.07 10:51)
Из Delphi Вижулбейсик делаешь?


 
oxffff ©   (2007-03-20 11:16) [9]

Если при освобождении произойдет exception, то другие объекты не будут освобождены.

destructor TDestroyer.Destroy;
begin
FreeAndNil(FObj);
end;

Если модифицировать так

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end;
end;

Лучше. Но контроля не будет.


 
wicked ©   (2007-03-20 11:25) [10]

кстати, исходный код при некоторых условиях можно было бы и переделать
так и просится вынести создание и удаление MyObj2 за цикл while, если он не строго завязан на счетчик цикла
тогда и не было бы этих вложенных try-finally

или, как вариант, оставить все как есть, только забрать внутренние try-finally, а во внешнем блоке finally проверять, удален ли MyObj2
например
MyObj1 := TMyObj1.Create;
try
  while <> do
  begin
    MyObj2 := TMyObject2.Create;
      ....
    FreeAndNil(MyObj2);
  end;
finally
  if Assigned(MyObj2) then
     MyObj2.Free;
  MyObj1.Free;

end;


и оффтопик
вот как надо извращаться, когда нету автоматических обьектов... :-)
например, ПОЛНОСТЬЮ аналогичный код на билдере
auto_pointer<MyObj1> = new TMyObj1();
 while(<>){
    auto_pointer<MyObj2> = new TMyObject2();
      ....
 }

auto_pointer - либо boost::shared_ptr (в написании могу ошибаться)
либо что нибудь похожее, smart pointer на строчек 100 кода (у меня свой, доморощенный)


 
Аноним   (2007-03-20 11:26) [11]


> destructor TDestroyer.Destroy;
> begin
> try
> FreeAndNil(FObj);
> except
> end;


Расстреливать.


 
oxffff ©   (2007-03-20 11:32) [12]


> Аноним   (20.03.07 11:26) [11]
>
> > destructor TDestroyer.Destroy;
> > begin
> > try
> > FreeAndNil(FObj);
> > except
> > end;
>
>
> Расстреливать.


Нужно аргументировать свою точку зрения.

А теперь по списку.

Посмотри реализацию _FinalizeArray.

Поэтому обертка поможешь, гарантированно освободить "все"  объекты в _FinalizeArray.

Поэтому думать нужно.
А потом делать такие громкие высказывания.


 
jack128 ©   (2007-03-20 11:37) [13]

wicked ©   (20.03.07 11:25) [10]
auto_pointer<MyObj1> = new TMyObj1();
while(<>){
   auto_pointer<MyObj2> = new TMyObject2();
     ....
}

Хм. А зачем вообще такой гемор, если можно просто написать:
TMyObj1 MyObj1;
?
oxffff ©   (20.03.07 11:13) [7]
исключения в деструкторах объектов практически невозможно коректно обработать. На это мона забить.


 
oxffff ©   (2007-03-20 11:38) [14]


> или, как вариант, оставить все как есть, только забрать
> внутренние try-finally, а во внешнем блоке finally проверять,
>  удален ли MyObj2
> например
> MyObj1 := TMyObj1.Create;
> try
>   while <> do
>   begin
>     MyObj2 := TMyObject2.Create;
>       ....
>     FreeAndNil(MyObj2);
>   end;
> finally
>   if Assigned(MyObj2) then
>      MyObj2.Free;
>   MyObj1.Free;
>
> end;


Если MyObj2.Free сгенерирует исключение, то ты благополучно покинешь
try finally и MyObj1.Free не будет и освобожден.


 
jack128 ©   (2007-03-20 11:39) [15]

Сергей М. ©   (20.03.07 11:00) [4]
Не проще ли сделать TMyObj1 и TMyObj2 наследниками TComponent и делать объект класса TMyObj1 владельцем всех объектов класса TMyObj2


А с TList/TStream и тд что делать?


 
wicked ©   (2007-03-20 11:42) [16]

> jack128 ©   (20.03.07 11:37) [13]

> Хм. А зачем вообще такой гемор, если можно просто написать:
>
> TMyObj1 MyObj1;

можно, не учел...
но в билдере есть "два вида классов" (цы) ИШ, и для унаследованных от TObject нужно делать именно через указатели, new и delete


 
Сергей М. ©   (2007-03-20 11:45) [17]


> jack128 ©   (20.03.07 11:39) [15]


Почему бы сразу не предупредить, что речь идет и о "генофонде" в том числе ?)


 
oxffff ©   (2007-03-20 11:46) [18]


> oxffff ©   (20.03.07 11:13) [7]
> исключения в деструкторах объектов практически невозможно
> коректно обработать. На это мона забить.


oxffff ©   (20.03.07 11:16) [9]
oxffff ©   (20.03.07 11:32) [12]

Либо переопределить _FinalizeArray о чем просят в qc.
Очент маловероятно, что они на это пойдут.
Но даже так много не добъешься.


 
Аноним   (2007-03-20 11:48) [19]


> Поэтому думать нужно.
> А потом делать такие громкие высказывания.


Думаем. Осознаем разницу между отсутствием ошибки и ее сокрытием.
PS. В деструкторах штатных исключений быть не должно. Такого мое категорическое мнение. Если там появляются исключения, программу нужно править. А для начала об этом исключении неплохо бы узнать.


 
oxffff ©   (2007-03-20 11:56) [20]


> Аноним   (20.03.07 11:48) [19]
>
> > Поэтому думать нужно.
> > А потом делать такие громкие высказывания.
>
>
> Думаем. Осознаем разницу между отсутствием ошибки и ее сокрытием.
>
> PS. В деструкторах штатных исключений быть не должно. Такого
> мое категорическое мнение. Если там появляются исключения,
>  программу нужно править. А для начала об этом исключении
> неплохо бы узнать.


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

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end;

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

Мы люди взрослые, поэтому выбирайте выражения.


 
jack128 ©   (2007-03-20 11:57) [21]

oxffff ©   (20.03.07 11:46) [18]
Но даже так много не добъешься.

ну методом copy-paste мона наплодить тучу таких структур :-)
TStringsRec = record
private
 FStrings: TStrings;
public
 contructor Create(AStrings: TString);
 destructor Destroy;
 class operator Implicit(const A: TStringsRec): TStrings;
end;


и юзать
var
 Strings: TStrings;
begin
 Strings := TStringsRec.Create(TStringList.Create);
 ...
end;

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


 
oxffff ©   (2007-03-20 12:01) [22]


> jack128 ©   (20.03.07 11:57) [21]
> oxffff ©   (20.03.07 11:46) [18]
> Но даже так много не добъешься.
> ну методом copy-paste мона наплодить тучу таких структур
> :-)
> TStringsRec = record
> private
>  FStrings: TStrings;
> public
>  contructor Create(AStrings: TString);
>  destructor Destroy;
>  class operator Implicit(const A: TStringsRec): TStrings;
>
> end;
>
> и юзать
> var
>  Strings: TStrings;
> begin
>  Strings := TStringsRec.Create(TStringList.Create);
>  ...
> end;
> Тут, так же как и с интерфейсами закладка идет на то, что
> записи будут уничтожаться именно при завершении процедуры.
>
> Так что извратов всяких мона придумать...


Не сработает. Нельзя определить деструктор для записи.


 
jack128 ©   (2007-03-20 12:03) [23]

oxffff ©   (20.03.07 12:01) [22]
Не сработает. Нельзя определить деструктор для записи.


Ты не понял. Это для случая, если коджир разрешит переопределять _FinallizeRecord.


 
Аноним   (2007-03-20 12:03) [24]


> oxffff ©  


> Связана с желанием хотя бы "улучшить" состояние с памятью


А зачем улучшать "состояние с памятью" в случае заведомого присутствия ошибки в программе? Ошибку надо править, и тогда состояние памяти само улучшится


> гарантировать отсутствие исключений не может


Не может. Поэтому желает о них узнавать. Что хуже - получить мем лик в режиме тестирования, или пропустить незамеченный баг в релиз?


> даже вы, умный вы наш.


Спасибо.


> Мы люди взрослые, поэтому выбирайте выражения.


ОК


 
jack128 ©   (2007-03-20 12:09) [25]

Так, обсуждение не туда скатывается.  По поводу _исходной_ реализации с интерфейсами - есть у кого какие соображения? В плане -
"Нет, это полный ацтой, потому то-потому то"  
   Или наоборот,
"Это рулезная идея, а то что, тот гаврик сказал, мол все плохо, так он - ламо потому-то, потому то"
:-)


 
oxffff ©   (2007-03-20 12:14) [26]


> Не может. Поэтому желает о них узнавать. Что хуже - получить
> мем лик в режиме тестирования, или пропустить незамеченный
> баг в релиз?


Я бы очень хотел с вами согласиться. Но не могу. Опыт не позволяет.

Программ без ошибок нет и не будет в ближайшее время.  

Так вы хотя бы позволите пользователю дорабоать с программой хотя бы в "полуштатном режиме".

А если следовать вашей точки зрения, то тогда зачем в релиз версии
зачем эти манипуляции с fs:[0] (try except, try finally)?

Ну не может все работать на все 100%. Дай бог, чтобы на 99.999%.
А что делать с одной сотой?
Похоже на errata в процессоре. Но никто не изымает процессоры.


 
oxffff ©   (2007-03-20 12:23) [27]


> jack128 ©   (20.03.07 12:09) [25]
> Так, обсуждение не туда скатывается.  По поводу _исходной_
> реализации с интерфейсами - есть у кого какие соображения?
>  В плане -
> "Нет, это полный ацтой, потому то-потому то"  
>    Или наоборот,
> "Это рулезная идея, а то что, тот гаврик сказал, мол все
> плохо, так он - ламо потому-то, потому то"
> :-)


Взгляд со стороны

Разница в коде?

1. MyObj2:=TObject.create;
  CreateDestroyerIntf(MyObj2);
  .......

2. MyObj2:=TObject.create;
  ......
  MyObj2.free;


 
Джо ©   (2007-03-20 12:27) [28]

> Разница в коде?

Разница не в этом, а в возможности избегать (вложенных) try/finally.


 
oxffff ©   (2007-03-20 12:35) [29]


> Разница не в этом, а в возможности избегать (вложенных)
> try/finally.


Аргументирую свою точку зрения.

_FinallizeRecord не содержит try  блока

Даже если мы модифицируем

destructor TDestroyer.Destroy;
begin
FreeAndNil(FObj);
inherited;
end;

на

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end
end;

Это не дает гарантии, что все финализуремые финализируются.
Могут быть другие интерфейсы, record, variants,string,DynamicArrays..
Которые возбудят исключение, а мы благополучно покинем _FinallizeRecord.

Объясните свою точку зрения.


 
oxffff ©   (2007-03-20 12:37) [30]


> благополучно покинем _FinallizeRecord.


Читать как "благополучно"  или неблагополучно


 
Ega23 ©   (2007-03-20 12:39) [31]

Всё-таки не совсем ясно, из-за чего весь сыр-бор.
Честно говоря, я мало где встречал конструкции типа

obj1 := TMyObj.Create;
try
 obj2 := TMyObj.Create;
 try
   obj3 := TMyObj.Create;
   try
      ...........
   finally
     obj3.Free;
   end;  
 finally
   obj2.Free;
 end;
finally
 obj1.Free;
end;


Да, с академической точки зрения такой код наиболее правильный (а то фиг его знает, чё там в конструкторе TMyObj.Create).
С другой стороны - ну максимум 2 вложенных try..finally + один try..except
Это лично у меня и то крайне редко. В VCL тоже подомных монстров не сильно замечал. Да и обкладывать всё на свете блоком try..except - ИМХО чистая паранойя. На небезопасных участках - да, безусловно.

ИМХО, сама по себе идея - ничё так. Но лично я бы не стал использовать. Мне проще и нагляднее try..finally написать.


 
Джо ©   (2007-03-20 12:43) [32]

> [29] oxffff ©   (20.03.07 12:35)
> Объясните свою точку зрения.

Я, наверное, не очень ее понял.

Вот код:

O1 := TObject.Create;
CreateDestroyerIntf(O1);
O1.SomeMethod; // допустим, тут будет исключение

O2 := TObject.Create;
CreateDestroyerIntf(O2);


Деструктор для O1 вызовется. Для O2, разумеется, не вызовется. Но он и не нужен, ибо O2 := TObject.Create ведь тоже не вызовется :)


 
Джо ©   (2007-03-20 12:44) [33]

> [31] Ega23 ©   (20.03.07 12:39)
> ИМХО, сама по себе идея - ничё так. Но лично я бы не стал
> использовать. Мне проще и нагляднее try..finally написать.

Придерживаюсь того же мнения :)


 
oxffff ©   (2007-03-20 12:53) [34]


> Джо ©   (20.03.07 12:43) [32]
> > [29] oxffff ©   (20.03.07 12:35)
> > Объясните свою точку зрения.
>
> Я, наверное, не очень ее понял.
>
> Вот код:
>
> O1 := TObject.Create;
> CreateDestroyerIntf(O1);
> O1.SomeMethod; // допустим, тут будет исключение
>
> O2 := TObject.Create;
> CreateDestroyerIntf(O2);
>
>
> Деструктор для O1 вызовется. Для O2, разумеется, не вызовется.
>  Но он и не нужен, ибо O2 := TObject.Create ведь тоже не
> вызовется :)


А c чего Деструктор для O1 вызовется?
Мы ведь покинем процедуру при O1.SomeMethod;
и до финализатора не доберемся

?

Или вы хотите модифицировать до

try

O1 := TObject.Create;
CreateDestroyerIntf(O1);
O1.SomeMethod; // допустим, тут будет исключение
O2 := TObject.Create;
CreateDestroyerIntf(O2);
except
end;

Но при

  finally
    obj3.Free;
  end;  
finally
  obj2.Free;
end;

obj2.Free выполниться при любом исходе выполнения obj3.Free

А Финализатор, такой гарантии не дает


 
Аноним   (2007-03-20 12:58) [35]


> Я бы очень хотел с вами согласиться. Но не могу. Опыт не
> позволяет.
>
> Программ без ошибок нет и не будет в ближайшее время.  
>
> Так вы хотя бы позволите пользователю дорабоать с программой
> хотя бы в "полуштатном режиме".
>
> А если следовать вашей точки зрения, то тогда зачем в релиз
> версии
> зачем эти манипуляции с fs:[0] (try except, try finally)?
>


А мне опыт как раз позволяет. Поясню:
Появление мем лика в полуштатном режиме при работе пользователя - это неприятность, но не такая большая, кроме того она проявится в заведомо ошибочной ситуации в программе, причем исключение  будет обусловлено именно ошибкой в программе.
Что касается

> А если следовать вашей точки зрения, то тогда зачем в релиз
> версии
> зачем эти манипуляции с fs:[0] (try except, try finally)?
>


Потому что ошибка может быть еще и в данных, что вписывается в понятие "штатный режим эксплуатации". А ошибка в коде в это понятие не вписывается.

Предположим вам надо в процедуре создать 5 экземпляров разных классов, и по окончании процедуры их разрушить
я имею в виду не в цикле, а типа

Obj1:=TObject1.Create;
Obj2:=TObject2.Create;
Obj3:=TObject3.Create;
Obj4:=TObject4.Create;
Obj5:=TObject5.Create;

Если заботиться о "улучшении памяти" по вашей концепции, то надо делать 5 вложенных блоков try-finally

Только честно - делаете? Везде?


 
Джо ©   (2007-03-20 13:03) [36]

[34] oxffff ©  
> А c чего Деструктор для O1 вызовется?
> Мы ведь покинем процедуру при O1.SomeMethod;

IntfClear вызовется до того, как мы «покинем процедуру». Вот вы попробуйте, попробуйте :)

П.С. Простой тест:

TMyObject = class (TObject)
public
  destructor Destroy; override;
end;

...

{ TMyObject }

destructor TMyObject.Destroy;
begin
 ShowMessage ("Destroyed")
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 O1: TMyObject;
begin
 O1 := TMyObject.Create;
 CreateDestroyerIntf(O1);
 raise Exception.Create("Exception raised");
end;



 
oxffff ©   (2007-03-20 13:15) [37]


> А мне опыт как раз позволяет. Поясню:
> Появление мем лика в полуштатном режиме при работе пользователя
> - это неприятность, но не такая большая, кроме того она
> проявится в заведомо ошибочной ситуации в программе, причем
> исключение  будет обусловлено именно ошибкой в программе.
>
> Что касается


"заведомо ошибочной ситуации в программе"?
Вы всегда можете указать на это место?
Даже после тестирования, ошибки остаются.

И наличие механизмов обеспечивающих robust при нештатных ситуациях является упреждающим шагом.

>Потому что ошибка может быть еще и в данных

Объект должен проверять данные, чтобы это не приводило к его краху.
А если он этого не делает, тогда это ошибка в коде. IMHO

>Только честно - делаете? Везде?

Да, я стараюсь придерживаться именно такой тактики.
При исключении, освободить все что нужно.
Ну и или попытаться это сделать, так точнее будет точнее.


 
oxffff ©   (2007-03-20 13:23) [38]


> Джо ©   (20.03.07 13:03) [36]
> [34] oxffff ©  
> > А c чего Деструктор для O1 вызовется?
> > Мы ведь покинем процедуру при O1.SomeMethod;
>
> IntfClear вызовется до того, как мы «покинем процедуру».
>  Вот вы попробуйте, попробуйте :)


О.
Я действительно не знал этого. Огромное спасибо за приобретенный skill


 
oxffff ©   (2007-03-20 13:36) [39]


> О.
> Я действительно не знал этого. Огромное спасибо за приобретенный
> skill


Да никогда не обращал внимание на код
после jmp @HandleFinally
а там он самый jmp -18 на финализатор.

Век живи, век учись. Спасибо Джо.


 
Джо ©   (2007-03-20 13:37) [40]

> [39] oxffff ©   (20.03.07 13:36)
> Век живи, век учись.

Дык, на то оно и автоматическое уничтожение интерфейсов в Делфи :)



Страницы: 1 2 вся ветка

Текущий архив: 2007.04.15;
Скачать: CL | DM;

Наверх




Память: 0.6 MB
Время: 0.07 c
2-1174988682
skye
2007-03-27 13:44
2007.04.15
Как вставить анимированную гифку в форму


1-1172004366
timself
2007-02-20 23:46
2007.04.15
Как перехватить начало перезагрузки?


15-1173695495
infom
2007-03-12 13:31
2007.04.15
Вот такие у нас программисты !


2-1175023480
Василий
2007-03-27 23:24
2007.04.15
округление


2-1175052215
bagos
2007-03-28 07:23
2007.04.15
drag drop формы