Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.08.02;
Скачать: [xml.tar.bz2];

Вниз

FreeAndNil против Free. Интересная концепция.   Найти похожие ветки 

 
Дмитрий Белькевич   (2009-05-29 12:47) [0]

Наткнулся на интересную статью:

http://gunsmoker.blogspot.com/2009/04/freeandnil-free.html

Посему хочу спросить - насколько автор прав? Действительно ли Free можно поменять везде на FreeAndNil? Можно ли придумать пример, где это будет работать неверно?

В целях развития идеи могу предложить следующее. Заменить глобально TObject.Free на вызов FreeAndNil (в тестовых целях, или перманентно). Плюсы такого подхода:

1. не нужно будет менять исходники - такое поведени можно сделать отключаемым.
2. можно будет добраться до компонент, у которых нет сырцов (это же и минус - их нельзя будет поправить, но, как минимум, поможет определить потенциально опасные компоненты).
3. не нужно будет трогать "генофонд" - чужие компоненты и компоненты делфи - но можно будет найти потенциально опасные места и в них (и, при желании, поправить).

Поменять, я думаю, можно так, как это делает RtlVclOptimize:

CodeRedirect(GetActualAddr(@System.LoadResString), @LoadResString);

Попробовал у себя в одном проекте поменять все вызовы Free на FreeAndNil. Ничего страшного не произошло. Массового падения не было, утечек тоже. Нашел место, где был доступ до рузрушенного объекта. Так что польза от такой замены определенно может быть.


 
TUser ©   (2009-05-29 12:56) [1]

Можно сделать так, что FreeAndNil даже не скомпилиться :)

type
TGluk = class
  procedure GlukProc;
  end;

procedure TGluk.GlukProc;
begin
 ShowMessage ("I""m gluk");
end;

procedure ShowGluk (const Gluk: TGluk);
begin
 Gluk.GlukProc;
 // Gluk.Free;
 FreeAndNil (Gluk);
end;

procedure TForm1.Button1Click(Sender: TObject);
var G: TGluk;
begin
 G := TGluk.Create;
 G.GlukProc;
end;


 
Ega23 ©   (2009-05-29 12:58) [2]

Знаешь, у меня вообще нигде FreeandNil нету. Вообще нигде. И ничего.


 
Игорь Шевченко ©   (2009-05-29 13:06) [3]


> Действительно ли Free можно поменять везде на FreeAndNil?
>  


нет конечно. FreeAndNil принимает var-параметр


 
Игорь Шевченко ©   (2009-05-29 13:10) [4]


> http://gunsmoker.blogspot.com/2009/04/freeandnil-free.html


"Другое дело, что сам with страшен. Вам не следует использовать его".

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


 
Дмитрий Белькевич   (2009-05-29 13:10) [5]

>Можно сделать так, что FreeAndNil даже не скомпилиться :)

Плюс подхода автора в том, что придётся заодно от потенциально опасных мест типа [1] избавляться.


 
Дмитрий Белькевич   (2009-05-29 13:13) [6]


> нет конечно. FreeAndNil принимает var-параметр


Неверно выразился. Имелось в виду - могут ли при этом возникнуть проблемы.

То, что FreeAndNil с const не соберется - я знаю.


 
Игорь Шевченко ©   (2009-05-29 13:20) [7]

Дмитрий Белькевич   (29.05.09 13:10) [5]

Нету плюсов в подходе автора. Не понимаю я таких, "более правоверных, чем Аллах".

Особенно умиляет наличие такого кода:

type
 TFoo = class
 private
    FBar: TBar;
 ..
 public
    constructor Create;
    destructor Destroy; override;
 end;

constructor TFoo.Create;
begin
 Fbar := TBar.Create;
 ...
end;

destructor TFoo.Destroy;
begin
  ...
  FreeAndNil(FBar);
  inherited;
end;


Спрашиваешь у автора - нафига FreeAndNil, тот на голубом глазу отвечает - а чтобы если кто обратится, то на недействителью ссылку не попадет...


 
Ганя   (2009-05-29 13:35) [8]


> Игорь Шевченко ©   (29.05.09 13:20) [7]



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


такое может быть - из inherited Destoy - в предке дергается виртуальный метод, который в потомке переопределен с обращением к FBar
вот и приплыли.

Но вообще конечно такое обобщение -  "всегда использовать FreeAndNil вместо Free" - глупость


 
Ega23 ©   (2009-05-29 13:37) [9]


> такое может быть - из inherited Destoy - в предке дергается
> виртуальный метод, который в потомке переопределен с обращением
> к FBar


На такие исключительные случаи можно написать
 FBar.Free;
 FBar := nil;


 
Игорь Шевченко ©   (2009-05-29 13:42) [10]

Ганя   (29.05.09 13:35) [8]


> в предке дергается виртуальный метод, который в потомке
> переопределен с обращением к FBar
> вот и приплыли.


Куда приплыли ?

FBar - private, о каких потомках может идти речь ? :)


 
KSergey ©   (2009-05-29 13:51) [11]

меня аргументы автора не убедили. Хотя то, что использовать FreeAndNil не ухудшит ситуацию - это да.


 
jack128_   (2009-05-29 13:51) [12]


> Куда приплыли ?

ты не понял.

TFooAncestor = class
protected
 procedure DoWork; virtual; abstract;
public
 destructor Destroy; override;
end;

destructor TFooAncestor.Destroy;
begin
 DoWork;
end;

TFoo = class(TFooAncestor)
private
 FBar: TBar;
protected
 procedure DoWork; override;
public
 destructor Destroy; override;
end;

procedure TFoo.DoWork;
begin
 ShowMessage(FBar.Name); // Если уничтожаем FreeAndNil(FBar) - то ошибка всплывет сразу.  Если же уничтожать через FBar.Free - то не факт
end;

destructor TFoo.Destroy;
begin
 FBar.Free;
 inherited;
end;


 
Плохиш ©   (2009-05-29 13:52) [13]


> Игорь Шевченко ©   (29.05.09 13:42) [10]


> FBar - private, о каких потомках может идти речь ? :)

Он говорит о той породе горекодеров, которые не знают, что у них делается в соседних строках глюкокода ;-)


 
Игорь Шевченко ©   (2009-05-29 14:03) [14]

jack128_   (29.05.09 13:51) [12]

Видишь ли, Женя, программу на Фортране можно написать на любом языке программирования. Дальнейшую дискуссию на эту тему считаю нецелесообразной, так как количество способов написать глючную программу, обойдя любые ламерские рекомендации (не использовать with, использовать freeandnil вместо free, еще что-нибудь из подобной оперы), заведомо превышает количество способов написать неглючную программу.

Плохиш ©   (29.05.09 13:52) [13]

Таким принудительное выпрямление рук не поможет, только ампутация. Желательно по самую голову.


 
Ганя   (2009-05-29 17:20) [15]


> Игорь Шевченко ©   (29.05.09 14:03) [14]


> Видишь ли, Женя,


А что именно в приведенном куске кода выдает "программу на фортране"?
Сам по себе факт вызова виртуального метода в деструкторе?
Или имя виртуального метода "DoWork"?
Давай заменим его на Clear
или на Disconnect
Поясните, где тут фортранность и кривизна рук?


 
Игорь Шевченко ©   (2009-05-29 17:24) [16]


> А что именно в приведенном куске кода выдает "программу
> на фортране"?


Необходимость для корректной работы сначала вызывать унаследованный деструктор, а затем освобождать собственные объекты.


 
TUser ©   (2009-05-29 17:42) [17]

Вообще, FreeAndNil явно опирается на парадигму "один объект - один указатель", то есть объект у нас де-факто - статический. Что явно противоречит идее динамических объектов, приятой в Delphi. Имхо.

Иными словами, есть на Obj1 у меня только одна ссылка, то это хорошо. А вот если где-то хранится ссылка ...


 
Юрий Зотов ©   (2009-05-29 18:03) [18]

Если человек пишет код бездумно, то и FreeAndNil ему не поможет.

Да, есть такая рекомендация - в деструкторе класс сначала чистит свое, и только потом вызывает inherited. Но это же не закон? Нет. И обязанность думать не отменяет.

В данном случае разработчик класса TFoo должен был бы сначала хорошо ознакомиться с классом-предком (вообще, при разработке класса-потомка знать особенности класса-предка даже не желательно, а обязательно), а потом написать так:

destructor TFoo.Destroy;
begin
 inherited;  
 FBar.Free;
end;

И все проблемы.


 
Игорь Шевченко ©   (2009-05-29 18:23) [19]

Юрий Зотов ©   (29.05.09 18:03) [18]

Законы определяются исключительно грамматикой языка и средствами библиотеки времени выполнения. Других законов попросту не существует, все остальное - рекомендации, корпоративные стандарты, правила хорошего тона и т.п. :)


> destructor TFoo.Destroy;
> begin
>  inherited;  
>  FBar.Free;
> end;


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


 
Юрий Зотов ©   (2009-05-29 18:28) [20]

> Игорь Шевченко ©   (29.05.09 18:23) [19]

> destructor TFoo.Destroy;
> begin
>  inherited;  
>  FBar.Free;
> end;

> у меня при взгляде на подобный код возникают подозрения в
> компетентности автора.

Это лишь потому, что в момент этого взгляда ты забываешь о том, что:

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

(c) Игорь Шевченко.

:o)


 
SPeller ©   (2009-05-29 19:59) [21]

От любых описанных проблем спасает простейший счетчик ссылок. Равно нулю - освобождаем, и пох кто там какие неверные ссылки запомнил - его проблемы, а не наши.


 
Нат ©   (2009-05-30 01:34) [22]

destructor TFoo.Destroy;
begin
inherited;  
FBar.Free;
end;

Припоминаю сообщение об утечках памяти при динамическом создании-удалении  TADOCommand. Именно по причине вызова inherited раньше прочего. Для сравнения, TAdoCustomDataSet вызывает inherited в последнюю очередь. Можно посмотреть куда-нибудь в корень... TPersistent, TComponent, TCollection...
Надо признать, там же масса примеров и обратного.
ИМХО, сперва почистить то, что знаешь (свое), а потом удалять самое себя более безопасный подход. Требовать же, корректно УбитьСебя, а потом убрать мусор... Подход истинного аристократа. ИМХО.
Пользуясь этой логикой, еще можно потребовать сначала создавать свои объекты, а потом уже вызывать создатель предка.

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


 
Германн ©   (2009-05-30 01:44) [23]


> Юрий Зотов ©   (29.05.09 18:28) [20]

+1
А по сабжу. Мне никогда не пришло на ум заменить Free на FreeAndNil.


 
Eraser ©   (2009-05-30 02:47) [24]

FreeAndNil помогает избежать багов (точнее более оперативно их находить) при повторном использовании переменных, что само по себе не лучший стиль программирования.


 
Юрий Зотов ©   (2009-05-30 19:36) [25]

> Нат ©   (30.05.09 01:34) [22]

> Требовать же, корректно УбитьСебя, а потом убрать мусор...

Хмм... а разве Вы не в курсе, что деструктор никаких "себя" вовсе не убивает?

Деструктор делает ровно то (и только то), что в нем написано. А что (и в каком порядке) в нем написать - вот как раз для этого программисту и дана голова. И если логика класса определяет, что сначала нужно вызвать унаследованный деструктор, то программист обязан именно так и написать. А если наоборот - значит, он должен написать наоборот.

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

(c) Игорь Шевченко.

:o)


 
Юрий Зотов ©   (2009-05-30 19:46) [26]

> По сабжу

От повсеместной замены Free на FreeAndNil, конечно, вряд ли станет хуже (правда, нужно иметь в виду потенциальный подводный камень, о котором здесь уже говорилось и на который мы с Andryk"ом когда-то реально напоролись).

Но если человек пишет код с головой, то и лучше от такой замены тоже не станет.

А если человек пишет код без головы, то и замена ему не поможет.


 
oldman ©   (2009-05-30 19:47) [27]


> Ega23 ©   (29.05.09 12:58) [2]
> Знаешь, у меня вообще нигде FreeandNil нету. Вообще нигде.
>  И ничего.


Поддерживаю всеми руками...


 
Нат ©   (2009-05-31 02:21) [28]


> деструктор никаких "себя" вовсе не убивает

Вернее сказать, не всякий объект нуждается в уничтожении.
"Себя" состоит из двух частей - унаследованная и добавленная.
Добавленную (если надо) уничтожаем сами.
Унаследованную (если надо) поручаем предку.
Здесь правильно говориться, мол "надо знать предков".
Однако, при любом знании, бывает и проруха.
В общем случае, правильнее и безопаснее вызывать метод предка. ИМХО.
Там где надо, будет подметено, там где не надо - не будет.
Если, конечно, нет желания, уничтожая объект, какие-то части оставить для дальнейшего...
constructor TObject.Create;
begin
end;
destructor TObject.Destroy;
begin
end;
procedure TObject.Free;
begin
 if Self <> nil then
   Destroy;
end;


 
Anatoly Podgoretsky ©   (2009-05-31 09:59) [29]

> oldman  (30.05.2009 19:47:27)  [27]

И у меня нет ни одного и никогда не было в нем нужды, хотя бы из-за потери типизации, в результате чего теряем проверку на стадии компиляции.
Эта процедура была придумана для ленивых или хуже.


 
Юрий Зотов ©   (2009-05-31 13:59) [30]

По сути, название процедуры неточно. Правильнее было бы назвать ее NilAndFree.


 
Нат ©   (2009-05-31 13:59) [31]

Каким образом обNILивание приводит к потере проверок?


 
Anatoly Podgoretsky ©   (2009-05-31 14:17) [32]

> Нат  (31.05.2009 13:59:31)  [31]

Таким, что параметр процедуры нетипизированый указатель.


 
Дмитрий Белькевич   (2009-05-31 15:25) [33]

>А вот если где-то хранится ссылка ...

И он разрушится...


 
Дмитрий Белькевич   (2009-05-31 15:55) [34]

>вообще, при разработке класса-потомка знать особенности класса-предка даже не желательно, а обязательно

Вообще, зачем тогда вообще нужна инкапсуляция? ИИХО, один из её плюсов - то, что потомок не должен заморачиваться о том, что делает предок у себя внутри. И это знание не только не нужно, но и вредно. Это, конечно, если предок грамотно разработан, и не даст потомку себя покалечить.

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

Free, кстати, тоже как бы процедура, и self в неё передаётся, так Free как бы еще и метод...

>А если наоборот - значит, он должен написать наоборот.

У меня так вообще кое-где условные вызовы предковых методов бывают ;) Правда, не в конструкторах/деструкторах.

>А если человек пишет код без головы, то и замена ему не поможет.

То есть вы (я так предполагаю, что вы пишете код с головой) пишете код вообще без ошибок? И с доступом к разрушенным объектам уже наццать лет не сталкивались? Это хорошо, конечно, если так. К сожалению, не у всех получается.
Я у себя как раз откопал одно место, где идёт такое обращение. И это место (страшно подумать) уже не менялось 4-5 лет... Если не больше.
Правил чужой код, в нём было условное разрушение объекта (не заметил), и через несколько строчек - мой доступ к нему.

>Таким, что параметр процедуры нетипизированый указатель.

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


 
@!!ex ©   (2009-05-31 16:19) [35]

> [34] Дмитрий Белькевич   (31.05.09 15:55)
> Free, кстати, тоже как бы процедура, и self в неё передаётся,
> так Free как бы еще и метод...

речь шла о ЛИШНЕМ вызове.
Как бы FreeAndNil - процедру и она вызывает Free.


 
Mystic ©   (2009-05-31 16:31) [36]

> И с доступом к разрушенным объектам уже наццать лет не сталкивались?

Бывает, но редко. И там, где сталкивался, то FreeAndNil не помог бы... Потому как обычно это происходит, когда на один объект указывает две (и более) ссылок. По одной он освободился, по другой используется...


 
Нат ©   (2009-05-31 17:38) [37]

Процедура-функция... одинаково лишний вызов.
Большая проблема - другие ссылки на удаленный объект.
Если ручками удаляем, значит, про этот указатель знаем и так.
Для уверенности обнилить его можно тут же.
Напрямую, без лишних вызовов, передачи нетипизированых аргументов...
Особой пользы от F&N...


 
@!!ex ©   (2009-05-31 17:46) [38]

> [37] Нат ©   (31.05.09 17:38)
> Процедура-функция... одинаково лишний вызов.

У меня видимо плохо со счетом:
Object.Free() - один вызов.

FreeAndNil(Object) - один вызов
Object.Free() - второй вызов.


 
Palladin ©   (2009-05-31 18:02) [39]

Object:=Nil третье присвоение )

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


 
Anatoly Podgoretsky ©   (2009-05-31 18:03) [40]

Лозунн - долой строгую типизацию.



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

Форум: "Прочее";
Текущий архив: 2009.08.02;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.008 c
8-1195925774
DmT
2007-11-24 20:36
2009.08.02
Как разместить объекты


15-1243695477
zdm
2009-05-30 18:57
2009.08.02
Windows 7 Delphi 2009 ошибка assertion failure


15-1243518797
Unknown user
2009-05-28 17:53
2009.08.02
Использование компилятора в своих программах


2-1244386325
demon
2009-06-07 18:52
2009.08.02
Прилипоние чужих окон


11-1204559200
MiniQ9
2008-03-03 18:46
2009.08.02
FreePascal - Linux - KOL, как подружить?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский