Comments 44
Есть такие, но из них извлечь реальную пользу, ничего не порушив, куда сложнее.
Хорошо в них то, что оба имеют дело уже с «боевым» объектом, причем если AfterConstruction возбуждает исключение, то сначала вызовется BeforeDestruction, а уж потом деструктор.
Почему-то у меня такое ощущение, что исключения принято вызывать, а возбуждают кой-чего другое. Но, если честно, Object Pascal использовал так давно, что не помню какой там для этого кейворд.
Учитывая что язык давно уже называется Delphi — неудивительно :)
А «вызов» — слово не слишком удачное, так как после возбуждения исключения в Delphi никакого возврата (как при «нормальных» вызовах) не происходит по определению.
Зря на C++ бочку катите, посмотрите, как управление памятью в Qt реализовано
Никто не отрицает, что на С++ можно и нужно сделать (и неоднократно сделано!) хорошую реализацию управления памятью. Но речь шла о якобы очевидном поведении объектов C++ при создании и уничтожении. Ничего очевидного там, естественно, нет. Жесткий порядок вызова при инициализации, смертельная опасность выброса исключения из конструктора, перезапись указателя VMT, фактическая невиртуальность вызовов оттуда же…
Всё одна глава в книжке и всё это будете знать. После этого всё кажется очевидно :)
Я это знаю давно. Но очевидным (т.е. однозначно понятным без точных знаний) оно от давности не стало.
Однозначно понятным без точных знаний не является ничего.
смертельная опасность выброса исключения из конструктора
W-w-what?
Формулировка и впрямь слишком резкая. Точнее, бросать исключения из конструктора в C++ требует гораздо больше труда и внимательности, так как деструктор при этом вызван не будет, а неинициализированные части объекта содержат мусор. В результате за очистку при прерывании инициализации отвечает конструктор, не имея вдобавок простых средств для отделения зерна от плевел.
Со стороны C++-программиста на проблему можно посмотреть по ссылке: alenacpp.blogspot.com/2006/11/blog-post.html
Деструкторы будут вызваны для всех полностью сконструированных членов класса и предков. Собственно главное — не бросать исключение в момент, когда какой-то объект находится в «промежуточном» состоянии. Для этого придумано RAII.
А для не полностью сконструированных конструктор должен все сделать сам. RAII помогает, конечно, но тем не менее дельфийский подход требует намного меньше труда и внимания — достаточно лишь аккуратно написать деструктор.
Соответственно, в деструкторе достаточно корректно обрабатывать нулевые значения, в чем сильно помогает процедура FreeAndNil, которая освобождает объект, только если ссылка на него уже не nil.

Точнее есть метод Free, который вызывает деструктор если Free был вызван для не нулевого объекта.
А процедура FreeAndNil еще дополнительно обнуляет объект который ей передали.

Да, имеет смысл упомянуть, что FreeAndNil еще и висячей ссылки за собой не оставляет.
Free не поминал специально, так как считаю его применение в деструкторах (и вообще везде, кроме тех мест где FreeAndNil задействовать невозможно) неправильным.
Почему — успешно объяснено до меня и без меня по ссылке: gunsmoker.blogspot.com/2009/04/freeandnil-free.html
Прочитал.
Участники дискуссии упускают из вида один важный момент (подробно освещенный по ссылке) — двойное удаление есть лишь частный случай использования висячей ссылки на объект, причем самый безобидный. Зато если после удаления будут вызваны любые другие методы объекта, то в случае использования FreeAndNil будет сразу возбуждено исключение, а Free эту ошибку замаскирует, причем наглухо — падать будет не всегда и совсем не там, где ошибка. Это куда более дорогая цена по сравнению с успешным двойным удалением (которое по факту все равно будет успешным однократным).
Почему-то мне кажется что топик несет в себе ноль полезной информации :(
В чем смысл-то? Перечисление «особенностей», о которых все знают?
возможность создания объектов с неизвестным на этапе компиляции классом
а можно пример?
Пример из RTL Delphi. Этот код создает класс при десериализации его из потока.
  procedure CreateComponent;
  var
    ComponentClass: TComponentClass;
  begin
    try
      ComponentClass := FindComponentClass(CompClass);
      Result := nil;
      if Assigned(FOnCreateComponent) then
        FOnCreateComponent(Self, ComponentClass, Result);
      if Result = nil then
      begin
        Result := TComponent(ComponentClass.NewInstance);
        if ffInline in Flags then
        begin
          Include(Result.FComponentState, csLoading);
          Include(Result.FComponentState, csInline);
        end;
        try
          Result.Create(Owner);
        except
          Result := nil;
          raise;
        end;
      end;
      Include(Result.FComponentState, csLoading);
    except
      if not Recover(Result) then raise;
    end;
  end;

Точно!
Спасибо.
Я ещё подумал про сериализацию (a la php), но забыл, что в Delphi тоже используется сериализация (при загрузке форм).
Соответственно, в деструкторе достаточно корректно обрабатывать нулевые значения, в чем сильно помогает процедура FreeAndNil, которая освобождает объект, только если ссылка на него уже не nil.

Delphi 7:

procedure FreeAndNil(var Obj);
var
 Temp: TObject;
begin
 Temp := TObject(Obj);
 Pointer(Obj) := nil;
 Temp.Free;
end;
Об «очевидном» и полезном или создание и уничтожение Delphi
FIXED
Delphi, ты еще жив?
В прошлом веке я так любил тебя. Ты был прекрасен.
Слухи о смерти Delphi оказались несколько преувеличены.
Хотя собственный родитель (Borland) приложил очень много усилий к организации «дельфицида».
Одна из самых больших профессиональных ошибок в моей жизни — это то, что в далеком 92-93 году я сориентировался на Borland.
А ведь тогда _нужно_ было предолеть рвотный рефлекс и пересесть на MS. Тогда фаворит был неочевиден а среда Turbo/Delphi давала 100 очков форы MS.
Кто бы знал, что Borland, с его грандиозными планами, уйдет в маргиналии.
Вряд ли ошибка (если это именно ошибка) была совершена в 92-93 — Delphi вплоть до седьмой версии включительно был более чем на уровне.
Оптимальное время миграции — выход Delphi 8 под .NET. Именно тогда был взят гибельный курс. А вообще лично я не вижу проблемы для программиста с переходом на .NET и сейчас (если, конечно, нет жестких ограничений по времени и заработку прямо сейчас). И не наблюдаю реальных альтернатив Delphi для Win32.
>А вообще лично я не вижу проблемы для программиста с переходом на .NET и сейчас
Ключевое слово «сейчас». Тогда в 94 найти работу было крайне сложно. Пришлось преквалифицироваться. Потом еще раз, и еще раз и еще раз… Десктопных приложений с тех пор не писал.
А перескочить с php или perl на .NET достаточно сложно. К тому же длительный перерыв в професии дает о себе знать. Но если бы тогда сориентировался на богомерзкий MS VBasic вместо Delphi (1/2/3) жизнь могла бы пойти по другому. Так мне кажется.
Сопссно, C# — это прямой наследник Delphi (или младший брат, учитывая общего родителя :)
Меня глючит. Кто общий родитель С# и Delphi???????????????

Вспомнился старый анекдот про лекцию Вирта(Хансена) в испанояычном университете.
Вирта: Вам нравится языка Паскаль?
Студенты: Си, синьор! Си! (sí señor! sí!)

Я вообще-то алгол учил в свое время. И до сих пор скобки расставляю по алгольному.
Все верно, от двойного удаления FreeAndNil не лечит, а маскирует.
Но любое другое использование висячей ссылки после освобождения маскирует уже Free, причем с куда худшими последствиями. Так что если код падает на Free, то сначала надо устранить двойное удаление, а уже потом заменить Free на FreeAndNil — для демаскировки использования висяка.
Only those users with full accounts are able to leave comments. Log in, please.