Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Delphi: Общие вопросы > Свойство-массив. Как правильно написать метод Free?


Автор: ttiger 13.04.09, 21:17
Для хранения динамических массивов, как свойств класса написал такой класс TIntegerValues64:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type
        TInt64_1DArray = array of Int64;


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      
    TIntegerValues64 = class
      private
        fArray: TInt64_1DArray;
        function GetCount(): Integer;
        procedure SetCount(const Value: Integer);
        function GetArray(Index: Integer): Int64;
        procedure SetArray(Index: Integer; Value: Int64);
      public
        property Elements[Index: Integer]: Int64 read GetArray write SetArray; default;
        property Count: Integer read GetCount write SetCount;
        procedure Add(const aValue: Int64);
        procedure Destroy;
        procedure Free;
      end;


Два вопроса:

1) Как правильно написать методы Create, Destroy и Free?
2) Не изобрел ли я велосипед? Это, ведь, очень похоже на TStringList, только про int64. Неужели нет в Delphi готового класса?

Листинг метода Add:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    procedure TIntegerValues.Add(const aValue: integer);
    begin
      SetLength(fArray, Length(fArray)+1 );
      FArray [Length(fArray)-1] := aValue;
    end;

Автор: MBo 14.04.09, 04:07
Free вообще писать не нужно.
Destroy - не просто процедура, а destructor

Конструктор и деструктор нужны, если требуется какая-то инициализация и финализация.

>SetLength(fArray, Length(fArray)+1 );
Так делать невыгодно - затраты памяти и времени будут велики. Стоит при необходимости увеличивать размер сразу вдвое или по крайней мере на значительно количество элементов.

Вообще смысла в этом классе пока немного. Просто массив позволяет делать все то же самое, никаких особых удобств класс не добавляет.
Если есть дополнительные соображения, расскажи.

Автор: Profi 14.04.09, 05:29
Для очистки динамического массива используется процедура Finalize.

Автор: --Ins-- 14.04.09, 06:49
Цитата MBo @
Destroy - не просто процедура, а destructor


Да еще и виртуальный, так что override написать не забыть.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    destructor Destroy; override;


А Free уже написан в TObject.

Автор: ttiger 14.04.09, 08:34
Цитата
Вообще смысла в этом классе пока немного. Просто массив позволяет делать все то же самое

Просто массив не может быть свойством, только полем. Если сделать его свойством, то не будет поэлементного доступа к его элементам.

Цитата
Destroy - не просто процедура, а destructor

Мне стыдно :wall:

Переделал:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    TIntegerValues64 = class
      private
        fArray: TInt64_1DArray;
        function GetCount(): Integer;
        procedure SetCount(const Value: Integer);
        function GetArray(Index: Integer): Int64;
        procedure SetArray(Index: Integer; Value: Int64);
      public
        property Elements[Index: Integer]: Int64 read GetArray write SetArray; default;
        property Count: Integer read GetCount write SetCount;
        procedure Add(const aValue: Int64);
        destructor Destroy; override;
        //procedure Free; _DELETED FROM IMPLEMENTATION
      end;


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    implementation
    destructor TIntegerValues64 .Destroy;
    begin
      Finalize(fArray);
      inherited destroy;
    end;

Проблема
при вызове метода Free не выполняется деструктор моего объекта и указателю на объект не присваивается nill

Автор: --Ins-- 14.04.09, 08:38
Цитата ttiger @
Finalize(fArray);


Не нужно, динамический массив финализируется при уничтожении объекта автоматически. А следовательно, можешь вообще деструктор не перекрывать, так как у тебя там только inherited.

Цитата ttiger @
при вызове метода Free не выполняется деструктор моего объекта


Не может быть. Или ты нам чего-то недоговариваешь, или твой вывод неверный.

Цитата ttiger @
и указателю на объект не присваивается nill


И не должен.

Автор: MBo 14.04.09, 10:11
>Просто массив не может быть свойством, только полем. Если сделать его свойством, то не будет поэлементного доступа к его элементам

класс TIntegerValues64 в таком виде не несет полезной нагрузки.
Если нужно индексированное свойство типа Int64 в каком-то действительно полезном классе, то и делай приватное поле - массив, индексированное свойство, геттер и сеттер. Создавать новую сущность я особого смысла не вижу.

Автор: ttiger 14.04.09, 10:20
Цитата
и делай приватное поле - массив, индексированное свойство, геттер и сеттер. Создавать новую сущность я особого смысла не вижу.

В классе, где используются эти свойства, таких две штуки публичных и три приватных.
Пять раз писать сеттер и пять раз геттер, прибавляя к ним процедуру добавления в конец - это вообще, нормально? А если одна ошибка п китайском коде? Распечатывать модуль и ВСЕ перечитывать?

Добавлено
Цитата --Ins-- @

Цитата (ttiger @ Сегодня, 11:34)
и указателю на объект не присваивается nill


И не должен.


Упс... У меня логика в методе следующая: в одном из свойств хранятся результаты подсчетов, притом они простои терируются. Перед тем, как повторно запускать подсчитывающий метод я "освобождаю" и снова создаю объект желаю таким образом очистить существующий, если есть и создать новый. Без явных доп. проверок.

Автор: MBo 14.04.09, 10:47
>Пять раз писать сеттер и пять раз геттер, прибавляя к ним процедуру добавления в конец - это вообще, нормально
ОК, разумно.
На больше подробностей пока раскрутить не удалось 8-)

Автор: ttiger 14.04.09, 11:05
Главный вопрос был в том, не изобрел ли я велосипед.
Хотелось удобства добавления в конец массива как в StringList. Раз все молчат, значит, не изобрел. Уже хорошо, а то месяц мучаюсь этим вопросом.

Навероное, мне нужен метод FreeAndNill. Как его правильно написать для моего класса?

Автор: CodeMonkey 14.04.09, 11:29
Вообще-то это процедура такая, FreeAndNil:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var
      Obj: TSomeClass;
     
    ...
      
      Obj := TSomeClass.Create;
     
    ...
     
      FreeAndNil(Obj); // рекомендуется использовать везде, где это возможно, вместо Obj.Free;

Автор: --Ins-- 14.04.09, 11:46
Цитата CodeMonkey @
// рекомендуется использовать везде, где это возможно, вместо Obj.Free;


Странная рекомендация, если рассматривать ее не в контексте задачи а глобально. ;)

Цитата ttiger @
Навероное, мне нужен метод FreeAndNill. Как его правильно написать для моего класса?


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

Автор: CodeMonkey 14.04.09, 12:09
Цитата ttiger @
Главный вопрос был в том, не изобрел ли я велосипед.

До D2009 - точно нет. В D2009 вместо самописного списка можно использовать TList<Int64>.

Цитата --Ins-- @
Странная рекомендация

Эээ.. чем?

Автор: --Ins-- 14.04.09, 13:07
Цитата CodeMonkey @
Эээ.. чем?


Ну, ты пишешь что рекомендуется использовать везде, а я этого не понимаю. Поясни :)

Автор: CodeMonkey 14.04.09, 13:48
Ну причина проста - нет никаких доводов так не делать :) Зато есть доводы против использования Free (ниже).

Можно возразить, что, к примеру, это не всегда строго необходимо - как, например, при удалении локальных переменных. Однако как раз к такому аргументу есть следующие возражения:
1). Локальную переменную могут потом сделать глобальной или частично-глобальной. FreeAndNil защитит нас от double-free. Просто Free - нет.
2). Очень большое количество кода получается использованием copy-paste. Если стоит Free, то, скопировав код в другое место (где у переменной другая область видимости) мы можем получить проблемы. С FreeAndNil таких проблем нет.
3). Единообразие стиля. Удобно, когда везде написано FreeAndNil вместо помеси Free/FreeAndNil. И сам не запутаешься, когда что ставить ("ааа, здесь надо FreeAndNil или можно просто Free?!!").
4). Экономия одного нескольких байт и одного лишнего вызова процедуры (т.е. если заменить FreeAndNil обратно на Free) не является аргументом в современных приложениях.

Автор: leo 14.04.09, 13:59
Цитата ttiger @
Перед тем, как повторно запускать подсчитывающий метод я "освобождаю" и снова создаю объект желаю таким образом очистить существующий, если есть и создать новый. Без явных доп. проверок.

Может проще задать метод Clear для очистки ? Если эти массивы используются в некотором классе, то можно не париться и сразу создавать их в конструкторе этого класса, а для очитски юзать Clear

Цитата ttiger @
Хотелось удобства добавления в конец массива как в StringList. Раз все молчат, значит, не изобрел

Для "тупого" добавления элемента достаточно написать одну процедуру
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    procedure AddToDynArray(var arr:TInt64DynArray;value:Int64); //overload;
    begin
      SetLength(arr,Length(arr)+1);
      arr[Length(arr)-1]:=value;
    end;

Поэтому создавать класс имеет смысл именно для устранения тупого наращивания длины массива при каждом добавлении элемента. А для этого нужно использовать принципы, заложенные в TList\TStringList, а именно разделить понятия емкости массива (Capacity) и текущего числа элементов в массиве (Count).

Автор: --Ins-- 14.04.09, 14:19
CodeMonkey, скажем так, для меня само повторное использование переменных - занятие нехорошее, по возможности стараюсь так не делать, а FreeAndNil соблазняет. Наверное поэтому и недолюбливаю. Второе: использовать FreeAndNil нужно аккуратно, потому что несмотря на название, там сначала происходит обнуление ссылки, а потом вызов Free. Если в деструкторе или методах из него вызванных нам ссылка нужна - приплыли. Сейчас точно не вспомню подробностей, но с такой ситуацией однажды сталкивался. Так что лучше все-таки задумываться, когда пишешь FreeAndNil, чем везде писать бездумно.

Автор: CodeMonkey 14.04.09, 14:41
Цитата --Ins-- @
Если в деструкторе или методах из него вызванных нам ссылка нужна - приплыли.

Это как раз плюс. Помогает отловить плохие ситуации. Потому что в описываемой тобой ситуации у тебя идёт ссылка через переменную, а не через Self. Заодно и избавимся от плохого стиля.

Цитата --Ins-- @
а FreeAndNil соблазняет

Эээ... чем соблазняет? :) Что Free, что FreeAndNil - я не вижу разницы для повторного использования переменной.

Цитата --Ins-- @
само повторное использование переменных - занятие нехорошее

Речь может идти не только о повторном использовании. Например, всевозможные ситуации типа "удалить объект, если его ещё не удалили".

Автор: Domino 14.04.09, 14:57
Для меня всегда FreeAndNil был скорее редким исключением, нежели правилом. Локальные объекты освобождаются с помощью Free в секции finally, поля-объекты - исключительно в деструкторе. Вопрос "удалить объект, если его еще не удалили" не стоит, скорее возникает ситуация "удалить объект, даже если его не создали".

CodeMonkey, сам вопрос достаточно интересен. Приведи, пожалуйста, небольшой пример оправданного использования FreeAndNil, моя точка зрения вполне может оказаться не самой верной :)

Автор: --Ins-- 14.04.09, 15:50
Цитата CodeMonkey @
Потому что в описываемой тобой ситуации у тебя идёт ссылка через переменную, а не через Self. Заодно и избавимся от плохого стиля.


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

Добавлено
Цитата Domino @
Для меня всегда FreeAndNil был скорее редким исключением, нежели правилом. Локальные объекты освобождаются с помощью Free в секции finally, поля-объекты - исключительно в деструкторе. Вопрос "удалить объект, если его еще не удалили" не стоит, скорее возникает ситуация "удалить объект, даже если его не создали".


Вот и у меня точно так же.

Автор: CodeMonkey 14.04.09, 16:46
Цитата --Ins-- @
Поле класса. Мы его занулили, потом вызвали деструктор, а внутренний класс может что-то потребовать у внешнего, в т.ч. через это поле.

А зачем это внешнему классу что-то требовать у своего поля в момент удаления этого поля? По-моему, это логическая ошибка в явном виде (обращение к объекту в момент его удаления, т.е. к частично-инициализированному объекту). И вот использование FreeAndNil как раз позволит отловить такие ситуации. Конечно, такая ситуация может быть заранее предусмотрена, но это совершенно некрасиво (шаг в сторону, любое изменение кода "не в тему" и ваш код перестанет работать). В любом случае, такая ситуация не плавающая и обнаружится сразу же. После чего вы спокойно вернёте Free на место.

Цитата Domino @
Для меня всегда FreeAndNil был скорее редким исключением, нежели правилом. Локальные объекты освобождаются с помощью Free в секции finally, поля-объекты - исключительно в деструкторе

Это слишком простая ситуация :) Если ВЕСЬ код действительно выглядит только так (и вы абсолютно точно в этом уверены, плюс исключаете всякую модификацию кода) - то большого смысла в FreeAndNil нет.

Цитата Domino @
Приведи, пожалуйста, небольшой пример оправданного использования FreeAndNil

Не очень понятно, какого рода пример тут можно привести. Ведь использование FreeAndNil вместо Free - это же совершенно опциональное действие. Не считая запутанных примеров (ну вот Ins дал про обращение в момент удаления), один и тот же корректный код будет работать совершенно одинаково, что с Free, что с FreeAndNil.
FreeAndNil чем-то подобен ремням безопасности: если прогон прошёл в штатном режиме - они не пригодились. Но если ваш код где-то напутал в последовательности действий, то FreeAndNil (как и ремни безопасности) защитят вас от последствий. Обнулив ссылку, FreeAndNil поможет поймать левое обращение сразу же, на месте. Без него код мог продолжить своё выполнение и дать неверный результат без возбуждения ошибки. Это опасно.

Как я уже сказал, с учётом чрезвычайно низких накладных расходов на FreeAndNil вместо Free лично я НЕ вижу довода против него.

Добавлено
Вы мне лучше приведите пример оправданного использования Free :D

Автор: leo 14.04.09, 17:55
Domino, --Ins--
Не нужно перегибать палку ;) И "повторное использование переменных" может использоваться довольно часто и соотв-но поля-объекты могут уничтожаться не только "исключительно в деструкторе". Типичный пример - "одноразовый" объект типа TThread, второй раз его (легально) не используешь, нужно уничтожать. Поэтому если за время жизни объекта-контейнера объект-поток может создаваться несколько раз, то соотв-но после его уничтожения соответсвующее поле нужно занулять. Аналогично при очистке\ресете состояния контейнера часть его полей-объектов может также очищаться (Clear, Reset и т.п.), а часть может и уничтожаться с занулением ссылок - все зависит от ситуации и конкретики.

Но рекомендовать повсюду совать FreeAndNil вместо Free - это ес-но перебор, хотя ничего ужасного в этом тоже нет и упираться изо всех сил, доказывая обратное я бы не стал ;)

Автор: Domino 15.04.09, 05:58
Цитата CodeMonkey @
Вы мне лучше приведите пример оправданного использования Free
Ну, как же без него? Ресурсы надо освобождать :)

Цитата leo @
Не нужно перегибать палку
leo, я и не перегибаю, даже наоборот. Я вовсе не являюсь яростным противником FreeAndNil (чего нельзя сказать, например, о конструкциях with и команде Exit ;)), даже использую его по необходимости. Услышал новую для себя точку зрения - повсеместное использование FreeAndNil вместо Free и захотел услышать доводы в пользу этого подхода.

Цитата CodeMonkey @
Как я уже сказал, с учётом чрезвычайно низких накладных расходов на FreeAndNil вместо Free лично я НЕ вижу довода против него.
CodeMonkey, рассуждения о накладных расходах оставим в стороне - сейчас не то время, чтобы настолько мелочно заботиться об экономии процессорных инструкций, это в любом случае не аргумент в пользу Free. Единственный плюс, какой я вижу - это AV по адресу 0 при обращении к уничтоженному объекту вместо малопонятных циферок и тем более непредсказуемых результатов выполнения. Это да, но и происходит такое только тогда, когда идет отступление от классического подхода - уничтожения объектов исключительно в деструкторе. Free пишу на автомате, не задумываясь. Писать на автомате FreeAndNil, хм... получаем более внятные ошибки и упрощается модификация кода.
Вообще, подход имеет право на жизнь, почему бы и нет? :) В ближайшее время надо будет опробовать.

Автор: Profi 15.04.09, 06:58
Цитата Domino @
команде Exit

А чем Exit-то не угодил? Да и With. Просто надо грамотна их использовать, а не тыкать куда придется. Например Exit удобно использовать в процедурах, где сначала цикл, и в случае если все элементы коллекции нас устраивают - какие-то действия. Если же хотя бы один элемент не устраивает, то проще вызвать Exit, чем делать лишнюю переменную для проверки. Ну, а With тут, например:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    with ListView1.Items.Add do begin
      Caption := "Новый";
      ImageIndex := 1;
    end;

Чем опять же заводить временную переменную (если конечно еще каких-то дальнейших действий с созданным Item'ом проводить не будет в этой функции).

Автор: Romkin 15.04.09, 06:59
Что, опять холивар?!
Как можно быть противником чего-то в языке программирования? Говорить, что являешься противником with и exit - все равно что говорить, что является противником слов "вместе" и "выход". Или, например, слова "концептуальный". Попробуйте быть противником repeat :)
Как можно быть противником слов - непонятно. Да, употреблять слова не к месту и не особо понимая их смысл плохо. Но не употреблять вообще означает обеднять язык.
Помните? "Крепкое словцо, вовремя и к месту сказанное, облегчает душу. Частая ругань лишает ругательство смысла. Примечание: ругань не сделает карты хорошими, а ветер - попутным".
Все, что есть в языке, надо применять. Должно быть применение нужного оператора в нужном месте.

Автор: CodeMonkey 15.04.09, 07:03
Цитата leo @
Но рекомендовать повсюду совать FreeAndNil вместо Free - это ес-но перебор

Ну а почему?

Вот все почему-то говорят: "зачем нам этот FreeAndNil? Ну не нужен он!" Хотя ни одного аргумента я так и не услышал. ИМХО, тут говорит скорее сила привычки, чем реальное положение дел.

Цитата Domino @
Free пишу на автомате, не задумываясь.

Вот она, сила привычки :) А вот у меня уже привычка писать FreeAndNil :) Тоже на автомате. Я себе даже Code Template на F + Tab забиндил: автоматом вставить FreeAndNil(Obj) и выделить Obj, чтобы я сразу впечатал имя переменной. Очень удобно.

Цитата Domino @
Ну, как же без него? Ресурсы надо освобождать

Имелось ввиду, аргументы на использование Free вместо FreeAndNil, конечно же.

Цитата Domino @
Это да, но и происходит такое только тогда, когда идет отступление от классического подхода - уничтожения объектов исключительно в деструкторе.

Нет, почему же. Представим, что в деструкторе объекта (объект-контейнер) мы удаляем объектное поле с помощью Free. В деструкторе этого поля вызывается виртуальный метод, который ничего не делает в базовом классе, а в каком-то далёком предке вызывает последовательность действий, которая приводит к вызову виртуального-же метода объекта-контейнера. Который, в свою очередь, в каком-нибудь далёком предке (ошибочно) обращается к нашему удаляемому полю. При этом, обращение проходит на ура, но общее состояние становится безвозвратно испорченным. Упс.
Почему я тут везде поставил виртуальные методы? Затем, чтобы нельзя было сказать: вот, смотрите, в моих классах нет нужды использовать FreeAndNil. Дело в том, что вы не можете этого знать! Кто-нибудь создаст наследника, в котором неаккуратно вызовет в деструкторе какой-нибудь метод, приводящий (быть может далеко косвенно) к чтению уже удалённых или находящихся в процессе удаления объектов. FreeAndNil защитит вас от такой ситуации, Free - нет.

К чему я всё это. Вот говорят (не только тут, на DK на меня тоже взбычились :) ): а вот в этой ситуации FreeAndNil - лишний! А чего ж вы тогда не используете вызов Destroy? Ведь Free во многих ситуациях тоже излишен.
Заметьте, что в 99% кода на Delphi деструктор объектов не вызывается вообще! Нас уже приучили использовать вызов процедуры Free. А ведь когда-то народ, просто привыкший везде писать Destroy, тоже кричал: "зачем нам этот Free? Не нужен он тут! Где надо, я сам всё поставлю!". Ну и что? Пишем же мы сейчас все Free? (ладно, вообще-то этот пример - не факт, а моя фантазия, т.к. я уже начал подзабывать - как оно там на самом деле было, в те времена. Но могу легко себе это представить, и, вроде бы, это кажется правдоподобным :D ).
Ну так вот я агитирую за то, чтобы сделать ещё шаг вперёд: использовать FreeAndNil вместо Free. Ведь польза от перехода Free -> FreeAndNil гораздо больше, чем польза от уже случившегося перехода от Destroy к Free.
В первом случае мы получаем авто-защиту от плавающих ошибок (это НЕ панацея, т.к. могут существовать множественные ссылки, но, тем не менее, существенный бонус). Во втором случае мы получили всего лишь возможность не писать явно if. Почему? Потому что если бы мы явно вызывали Destroy, вместо Free, то мы запустили бы деструктор с Self = nil, что немедленно привело бы к AV при первом же обращении к полю объекта. Ошибка совершенно не плавающая и легко отлавливается. Согласитесь, что бонус от первого перехода намного более существенен, чем бонус от второго.
Так почему же медлить? Почему бы, начиная с сегодняшнего дня, везде не использовать FreeAndNil вместо Free? Если вы сделаете это своей привычкой - вы ничего не потеряете, а, наоборот, приобретёте немалые бонусы.

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

Добавлено
Цитата Romkin @
Что, опять холивар?!

Это не холивар, мои аргументы неоспоримы :lool:

Автор: --Ins-- 15.04.09, 07:05
Цитата Profi @
А чем Exit-то не угодил? Да и With.


Сейчас кто-нибудь еще и goto вспомнит :D

Автор: CodeMonkey 15.04.09, 07:11
Цитата Profi @
Да и With. Просто надо грамотна их использовать, а не тыкать куда придется.

Проблема в том, что вы никак это не объясните большинству программистов на Delphi, которые являются батоно-давителями и уж конечно не читают форумы/блоги/книги по программированию. With не страшен, скажем, вам или мне (хотя я тоже на нём обжигался), но он плох в целом для Delphi-сообщества.
Вон, си тоже безопасный язык, если грамотно его использовать. Проблема только в том, что если у вас есть возможность что-то сделать не правильно - это обязательно сделают неправильно! Поэтому ситуация с сями и with больше похожа на обезьяну с гранатой. Это - тень из прошлого, когда господствовала идеология "мы даём программисту в распоряжение самые мощные инструменты, а он обязуется использовать их верно". История показала несостоятельность и наивность такой точки зрения. Поэтому в последние годы курс меняется на "если вы не хотите делать правильно - вас заставят это делать" (и это не только в программировании). И это правильно. Разработчики Delphi уже давно выкинули бы этот with, если бы.... не совместимость со старым кодом.

P.S. Что не так с exit - я пока ещё сам не понял :D

Автор: --Ins-- 15.04.09, 07:14
Цитата CodeMonkey @
ИМХО, тут говорит скорее сила привычки, чем реальное положение дел.


Соглашусь, скорее всего. Ну, а чтобы менять привычку причина должна быть очень веской. ;)

Цитата CodeMonkey @
Ведь польза от перехода Free -> FreeAndNil гораздо больше, чем польза от уже случившегося перехода от Destroy к Free.


Зря недооцениваешь преимущества Free перед Destroy. Free вместо Destroy писать обязательно в деструкторе класса. Почему? Потому что никто не отменял исключения в конструкторе. При исключении в конструкторе деструктор вызывается автоматом, а если ссылка на тот момент еще не инициализирована, то ловим новое исключение - AV. Ну, а учитывая что (по крайней мере у меня) 90% вызовов Free стоит именно в деструкторах, то это очень даже актуально. Так что не стал бы так оценивать пользу.

Добавлено
Цитата CodeMonkey @
что если у вас есть возможность что-то сделать не правильно - это обязательно сделают неправильно!


Кстати, с этой фразы начинается сборник законов Мерфи :yes:

Автор: CodeMonkey 15.04.09, 07:17
Цитата --Ins-- @
Сейчас кто-нибудь еще и goto вспомнит

Хотя про goto в целом справедливо всё то же, что и для with, но сегодня goto не злоупотребляют вообще (можете привести пример из практики?), в отличие от with. Ну а раз не злоупотребляют - то какой же от него вред?
Если уж совсем жёстко - то его бы убрать (следуя той же логике "потенциально опасен"), но тут уже я против - сам его использую иногда :) Позволяет красиво выйти из нескольких циклов, не вводя процедуру или переупорядочивая код.

Автор: --Ins-- 15.04.09, 07:18
Цитата CodeMonkey @
Позволяет красиво выйти из нескольких циклов, не вводя процедуру или переупорядочивая код.


Abort в помощь :ph34r:

Автор: CodeMonkey 15.04.09, 07:20
Цитата --Ins-- @
причина должна быть очень веской

Это верно. Осталось дождаться, когда какой-нибудь косяк из-за отсутствия FreeAndNil не ударит по носу ;)

Цитата --Ins-- @
Зря недооцениваешь преимущества Free перед Destroy

Я ни в коем разе не имел ввиду деструктор! Я сказал: во многих случаях. Случай удаления частично-инициализированного объекта в них, разумеется, не входит.

Добавлено
Цитата --Ins-- @
Abort в помощь

ИМХО, стрельба из пушки по воробьям.
Вот когда из нескольких процедур одним махом - это да. А так - зачем? Да и пишется это невнятней goto. (не буду говорить про накладные расходы, просто надо иметь ввиду возможность наличия всяческих утилит диагностики исключений, который вполне могут выполнять кучу работы при raise).

Автор: Profi 15.04.09, 07:25
Цитата CodeMonkey @
Осталось дождаться, когда какой-нибудь косяк из-за отсутствия FreeAndNil

У меня был! Был такой код (не мой):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    firstObject := nil;
    secondObject := nil;
    try
      firstObject := TFirstObject.Create;
      ...
      if ... then
      begin
        secondObject := TSecondObject.Create;
      ...
      end;
      ...
    finally
      firstObject.Free;
      secondObject.Free;
    end;

Я не вдавался в логику (не было времени, надо было отловить AV), так как работа функции заказчика устраивала. А потому, просто заменил код на:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    FreeAndNil(firstObject);
    FreeAndNil(secondObject);

и AV исчезло 8-).

Автор: --Ins-- 15.04.09, 07:28
Цитата CodeMonkey @
ИМХО, стрельба из пушки по воробьям.


Ну, а мне видится это как использование инструмента по назначению. Какой смысл инструмента "исключение"? Уйти от сложного кода с условиями. А вместо этого локализовать код обработки внештатной ситуации. Я уже привык, скажем, при использовании WinAPI писать Win32Check вместо тучи проверок условий. Здесь - все тоже самое. Да, goto не использую по религиозным соображениям, потому как вижу более подходящий инструмент, не нарушающий структурность кода.

Автор: CodeMonkey 15.04.09, 07:29
В тему:
http://xkcd.com/386/
:lool:

Добавлено
Цитата --Ins-- @
Ну, а мне видится это как использование инструмента по назначению.

Тоже верно. За сим предлагаю тему с goto и закрыть - аргументы все высказали и со всем согласились :)

Автор: --Ins-- 15.04.09, 07:32
Цитата Profi @
Я не вдавался в логику (не было времени, надо было отловить AV), так как работа функции заказчика устраивала.


Мда. Если ты на стороне противников FreeAndNil, то довод убедительный, если ты сторонник FreeAndNil, то ты свой игрок в чужой команде :D Я не разобрался и у меня заработало - это значит ты замаскировал ошибку, но не исправил ее. Это все равно, как если бы написал пустой обработчик исключения, и AV тоже перестало бы вылетать. Извини, не наш метод ;)

Добавлено
ЗЫ: Я не противник FreeAndNil, я просто не использую его без явной на то необходимости

Добавлено
Цитата CodeMonkey @
За сим предлагаю тему с goto и закрыть


Согласен, а то может увлечь :D

Автор: CodeMonkey 15.04.09, 07:51
Цитата --Ins-- @
это значит ты замаскировал ошибку

Смотря что считать ошибкой ;)

В данном примере могло быть (только например! не надо педантично придираться :) ) так:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    firstObject := nil;
    secondObject := nil;
    try
      firstObject := TFirstObject.Create;
      ...
      if ... then
      begin
        secondObject := TSecondObject.Create;
      ...
      end;
      ...
      if .... then // редко выполняемое условие
        secondObject.Free;
      ...
    finally
      firstObject.Free;
      secondObject.Free;
    end;


Да, ошибка. Но FreeAndNil её снимает. Да, возможно, остаётся логическая ошибка. Но не фактическая.

В любом случае, практика маскировки ошибки не имеет никакого отношения к FreeAndNil vs Free! :angry:
:D )))

Автор: Profi 15.04.09, 07:52
Цитата --Ins-- @
Я не разобрался и у меня заработало

Я не сказал, что я не разобрался. Кстати, не дописал, код такой:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    firstObject := nil;
    secondObject := nil;
    for ...
      try
        firstObject := TFirstObject.Create;
        ...
        if ... then
        begin
          secondObject := TSecondObject.Create;
        ...
        end;
        ...
      finally
        firstObject.Free;
        secondObject.Free;
      end;

Сразу кидается в глаза то, что второй объект может быть не создан, но метод Free вызовет Destroy, так как на предыдущей итерации цикла, он был создан и уничтожен, но указатель не был обнулен. Вот и все. Код же, до finally отрабатывается корректно, в нем учитывается создан ли второй объект или нет. Ясное дело, что можно было еще в if добавить блок try finally и уничтожать второй объект в нем через Free. Но из-за нехватки времени, мне было проще заменить две строки, чем переписывать все логику цикла.

Добавлено
Смотрю какая-то началась на форуме нездоровая тенденция: пытаться уличить оппонента в некомпетентности. Не надо переносить суда привычки с другого форума.

Автор: leo 15.04.09, 07:56
Цитата Domino @
отступление от классического подхода - уничтожения объектов исключительно в деструкторе

Интересно, откуда взялся этот "классический подход" ;) Вот на скорую руку пробежался по classes и controls.pas в D7 и нашел немало мест удаления объектов-полей с занулением ссылок, в частоности в TComponent.Remove происходит удаление списка FComponents при Count=0.
Я в своих "супер-пупер" контейнерах при очистке (Clear) также частенько удаляю некоторые объекты либо потому, что они используются не всегда, а от случая к случаю, либо занимают достаточно памяти, а писать для них метод Clear накладно - проще грохнуть и создать заново ;)

Цитата CodeMonkey @
Цитата leo @
Но рекомендовать повсюду совать FreeAndNil вместо Free - это ес-но перебор
Ну а почему?

Пащиму - патамушта ;)
Потому, что использование Free вместо Destroy, во-первых, настоятельно рекомендуется самим Борманом, во-вторых, набрать 4 буквы, да еше и расположенных рядышком на клавиатуре, проше и быстрее, чем 7 ;)
А FreeAndNil и набирать дольше и никаких настоятельных рекомендаций по поводу ее использования нет - хочешь юзай, не хочешь не надо, все на твое усмотрение. Разумеется когда нужно обнулить ссылку после Free, то проще юзать FreeAndNil. Поэтому выступать за или против FreeAndNil - просто, как-то глупо ;)

PS: Кстати FreeAndNil появилась сравнительно "недавно", по крайней мере в D3 ее не было и люди как то без нее обходились ;) А у меня, например, до сих пор "тащится" парочка долгоиграющих супер-проектов на D3 и поэтому привычка к юзанию FreeAndNil в D7 акуается досадными эрррорами в D3 ;)

PPS: Мда, скоко понаписали то, не угнаться за полетом мысли :)

Добавлено
Цитата Profi @
Кстати, не дописал, код такой

А правильно было бы как то так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for ...
    begin
      secondObject:=Nil;    
      firstObject := TFirstObject.Create;
      try
        ...
        if ... then
        begin
          secondObject := TSecondObject.Create;
          ...
        end;
        ...
      finally
        firstObject.Free;
        secondObject.Free;
      end;
    end;

Автор: CodeMonkey 15.04.09, 08:14
Цитата leo @
Разумеется когда нужно обнулить ссылку после Free, то проще юзать FreeAndNil

Речь идёт о том, что это не всегда бывает видно. Взяв в привычку повсеместно писать FreeAndNil вы избавляете себя от этих проблем. И не надо ломать голову.
Проблема даже в том, что если вы привыкли использовать Free, то во большинстве случаем вам даже не придёт в голову задуматься о том: а не нужен ли здесь FreeAndNil? И вот отсюда-то и лезут проблемы.
Как я уже сказал: FreeAndNil - это ремни безопасности. Осталось только это осознать.

Цитата leo @
Потому, что использование Free вместо Destroy, во-первых, настоятельно рекомендуется самим Борманом

Я уже привёл аргументацию, что переход Free -> FreeAndNil имеет бОльшую ценность, чем переход Destroy -> Free. С учётом этого, аргумент отсутствия официального "добро" на FreeAndNil выглядит бледно ;)

Цитата leo @
акуается досадными эрррорами в D3

Не, ну совсем до детского сада-то не стоит опускаться ;) Ничто не мешает добавить FreeAndNil в отдельный модулёк Compatibility.pas. В старших версиях Delphi есть ведь много и других полезных функций, которых нет в D3 - вот и их туда же заодно. Зачем же добровольно лишать себя сладкого?

Автор: leo 15.04.09, 08:26
Цитата CodeMonkey @
Зачем же добровольно лишать себя сладкого?

Чтобы не растолстеть и не потерять тонус ;)

Цитата CodeMonkey @
Ничто не мешает добавить FreeAndNil в отдельный модулёк Compatibility.pas

Открою страшную тайну - я в те стародавние времена своим детским умом допер до "преимуществ" FreeAndNil и юзал собственную аналогичную ф-ю ClearObject, и кстати также совал ее где надо и не надо. Потом как-то все устаканилось и руки сами собой пишут где нужно Free, а где то ClearObject\FreeAndNil

Цитата CodeMonkey @
Взяв в привычку повсеместно писать FreeAndNil вы избавляете себя от этих проблем. И не надо ломать голову.

"Проблема" в том, что твои рекомендации хороши для "детского сада", для вьюношей "пылких со взором горящим", "обдумывающих житье" и т.д. и т.п. А спорить ты пытаешься с людьми, которые (видимо) не одну собаку и не один пуд соли съели и без всякого масла в виде FreeAndNil. И соотв-но менять свои привычки (видимо) не собираются. Поэтому и спор собс-но говоря не уместен. Пиши статейку в FAQ для бегинеров и усё ;)

Автор: CodeMonkey 15.04.09, 08:47
Цитата leo @
спорить ты пытаешься с людьми, которые (видимо) не одну собаку и не один пуд соли съели и без всякого масла в виде FreeAndNil

Я только повторю свой вопрос: ну и что же вы, Destroy-то не используете, где это можно?

Цитата leo @
И соотв-но менять свои привычки (видимо) не собираются.

Об этом я и говорю: что единственная причина использовать Free - это привычка.

Цитата leo @
Поэтому и спор собс-но говоря не уместен.

Да не спорю я ни с кем и уж тем более никого не убеждаю менять свои привычки. Если вы посмотрите, с чего всё началось - меня попросили обосновать эту точку зрения. Всё. Я весь топик повторяю одну и ту же мысль, перекраивая на все лады. Потому что постоянно кто-то влезает и вставляет свои пять копеек (пример с вами: "совать FreeAndNil вместо Free - это ес-но перебор") ;) Вот собственно и всё.

Автор: --Ins-- 15.04.09, 08:52
Цитата CodeMonkey @
Потому что постоянно кто-то влезает и вставляет свои пять копеек


Это нормально. Любая мысль, высказанная публично, может быть оспорена.

Автор: CodeMonkey 15.04.09, 09:00
Да не вопрос.
Но аргументов-то нет. Видимо, просто не нравится такое мнение - вот нужно обязательно сказать, что это всё зря. Ну а мне это как видится? Что мои слова остались не поняты - значит их нужно сказать по-другому.

P.S. Посему, пожалуй, мне стоит закончить.

Автор: leo 15.04.09, 09:23
Цитата CodeMonkey @
Я только повторю свой вопрос: ну и что же вы, Destroy-то не используете, где это можно?

Я уже объяснил выше: писать Free короче, особенно с учетом того, что она производит проверку объекта на Nil

Цитата CodeMonkey @
Об этом я и говорю: что единственная причина использовать Free - это привычка

С этим никто и не спорит, поэтому чтобы сломать эту привычку нужны достаточно веские аргументы, которых, увы, нет и быть не может ;)

Цитата CodeMonkey @
Если вы посмотрите, с чего всё началось - меня попросили обосновать эту точку зрения. Всё. Я весь топик повторяю одну и ту же мысль, перекраивая на все лады

Началось все с фразы
Цитата CodeMonkey @
FreeAndNil(Obj); // рекомендуется использовать везде, где это возможно, вместо Obj.Free;

Уже к самой формулировке можно придраться. Кем рекомедуется ? Борландом и Ко, мировым дельфийским сообществом ? Или конкретно c великим гуру CodeMonkey, с которым в данном вопросе и на ДК не все согласны ;)
И "пять копеек" нужно внимательне рассматривать и цитировать. Пример со мной: я сказал не просто "совать", а "повсюду совать", специально выделив слово "повсюду" - чувствуешь разницу ? Немудрено, что при таком невнимательном и неаккуратном стиле рекомендуется использовать костыли под названием FreeAndNil :D

PS: Да, пора завязывать с этим делом. Все все поняли и учли на светлое будущее ;)

Автор: Domino 15.04.09, 09:24
Цитата Profi @
А чем Exit-то не угодил? Да и With.
По with CodeMonkey всё хорошо расписал, а на часть по Exit можно ответить вашими же словами:
Цитата Profi @
Просто надо грамотна их использовать, а не тыкать куда придется.
С помощью Exit очень удобно множить неочевидность в коде, наличие этой команды почти всегда является признаком необходимости рефакторинга.
Цитата CodeMonkey @
Представим, что в деструкторе объекта (объект-контейнер) мы удаляем объектное поле с помощью Free. В деструкторе этого поля вызывается виртуальный метод, который ничего не делает в базовом классе, а в каком-то далёком предке вызывает последовательность действий, которая приводит к вызову виртуального-же метода объекта-контейнера. Который, в свою очередь, в каком-нибудь далёком предке (ошибочно) обращается к нашему удаляемому полю. При этом, обращение проходит на ура, но общее состояние становится безвозвратно испорченным. Упс.
Отличный пример, хоть сразу в учебник не вставляй :) Спасибо за объяснения.

Автор: leo 15.04.09, 09:38
CodeMonkey
PPS: В принципе я с тобой согласен, поэтому чтобы завершить дискуссию на позитивной ноте, (слегка поморщившись) могу присоединиться к твоей рекомендации для бегинеров - юзать FreeAndNil вместо Free :yes:

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)