Форум: "Прочее";
Текущий архив: 2007.04.15;
Скачать: [xml.tar.bz2];
ВнизАвтоматическое уничтожение объектов Найти похожие ветки
← →
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)
> Век живи, век учись.
Дык, на то оно и автоматическое уничтожение интерфейсов в Делфи :)
← →
ANB © (2007-03-20 13:43) [41]http://www.softwarer.ru/memory.html
Тута разжевано все. ИМХО - в делфи уже есть все, что надо.
Код вида :
Obj1 := nil;
Obj2 := nil;
Obj3 := nil;
try
Obj1 := TObj1.Create;
Obj2 := TObj2.Create;
Obj3 := TObj3.Create;
...
куча полезного кода
finally
FreeAndNil(Obj1);
FreeAndNil(Obj2);
FreeAndNil(Obj3);
end;
практически безопасен с точки зрения утечки.
Ну если сильно очень бояться, можно свой MyFreeAndNil написать с подавлением исключений.
← →
oxffff © (2007-03-20 13:49) [42]
> Джо © (20.03.07 13:37) [40]
> > [39] oxffff © (20.03.07 13:36)
> > Век живи, век учись.
>
> Дык, на то оно и автоматическое уничтожение интерфейсов
> в Делфи :)
И не только их.
Вот как они "хитро" переходят на следущую за jmp @HandleFinally инструкцию
Вот код в HandleFinally
ADD ECX,TExcDesc.instructions (5 байт)
call ecx
← →
Джо © (2007-03-20 13:57) [43]> [42] oxffff © (20.03.07 13:49)
> Вот как они "хитро" переходят на следущую за jmp @HandleFinally
> инструкцию
Действительно, «хитро».
← →
Loginov Dmitry © (2007-03-20 15:30) [44]> [41] ANB © (20.03.07 13:43)
+1
А то навыдумывали тут кто на что горазд ;)
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2007.04.15;
Скачать: [xml.tar.bz2];
Память: 0.6 MB
Время: 0.041 c