Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Стиль: Вызов виртуальных методов из деструкторов


Страницы:   1 2 3

Igorek ©   (2002-10-24 13:45:00) 

Поясню на примере: есть класс A, от него наследуется класс B. В классе А есть виртуальная ф-ция f, которая переопределена в насленике В. Причем переопределенный вариант работает с полями, которые есть в классе В. А в деструкторе класса А она вызывается. Теперь представим уничтожение класса В. Сначала очищаются поля в своем классе, потом вызывается деструктор предка (и через него функция f соответственно). И поскольку она виртуальная, то вызовется переопределенный вариант из В, который обратиться к уже очищенным полям из В.
Варианты выхода из ситуации:
1) избегать прямого или косвенного вызова виртуальных функций из деструкторов вообще (а для этого иногда дублировать код в деструкторах)
2) давать этим функциям знать, не из деструктора ли они вызываются (ввести в класс флажок Destroing или добавить флажок параметр CalledFromDestructor)
3) разделять функциональность ф-ции f на ту, которая выполняется всегда, и на ту, которая выполняется только при вызове не из деструктора; т.е. еще две функции, первую из которых и вызывать из деструктора; но может случиться, что f придется разделить более чем на две части (если они идут непоочередно).

Вот такой код на Паскале:

A = class
procedure f; virtual;
destructor Destroy; virtual;
end;
B = class(A)
procedure f; override;
destructor Destroy; override;
end;

destructor A.Destroy;
begin
f;
end;

destructor B.Destroy;
begin
inherited;
end;

Если компилировать и трассировать на Делфи 6, то увидим, что при уничтожении обьекта класса В, порядок вызовов будет такой
-В.Destroy
-A.Destroy
-B.f

Народ, как вы считаете, какой выход лучше всего? И не является ли приметой плохого стиля необходимость вызова виртуальных методов из деструкторов вообще?


Ученик   (2002-10-25 12:31:00) [41]

>Юрий Зотов © (25.10.02 12:17)

"...да и вообще этот вызов не обязателен)."

Что Вы имеете ввиду ?


Юрий Зотов   (2002-10-25 12:17:00) [42]

1. Вызов виртуальных (как и любых других) методов из деструктора - это нормальная практика, и примеров тому полно в VCL. Просто надо проследить за правильным порядком вызовов, вот и все (ведь никто не заставляет вызывать inherited строго последним, или строго первым, да и вообще этот вызов не обязателен).

2. Если класс компонентский, то Флажок csDestroying уже есть в ComponentState.


VaS   (2002-10-25 11:44:00) [43]

Немного не так :) Вот, например, имеем в В TList, с которым работаем в виртуальной f() и уничтожаем в деструкторе:


A = class
procedure f; virtual;
destructor Destroy; override;
end;

B = class(A)
private
l: TList;
public
procedure f; override;
constructor Create;
destructor Destroy; override;
end;

{ A }

destructor A.Destroy;
begin
f;
inherited;
end;

procedure A.f;
begin
end;

{ B }

constructor B.Create;
begin
l:=TList.Create;
end;

destructor B.Destroy;
begin
inherited;
l.Free;
end;

procedure B.f;
begin
inherited;
l.Clear;
end;

end.


Т.е. вызываем inherited Destroy доочистки специфичных для дочернего класса данных (полей).


VaS   (2002-10-25 11:29:00) [44]

А так?


A = class
procedure f; virtual;
destructor Destroy; override;
end;
B = class(A)
procedure f; override;
destructor Destroy; override;
end;

destructor A.Destroy;
begin
inherited;
f;

end;

destructor B.Destroy;
begin
inherited;
end;


Skier   (2002-10-25 10:52:00) [45]

>Igorek

> И поскольку она виртуальная, то вызовется переопределенный
> вариант из В, который обратиться к уже очищенным полям из
> В.


Почему ?


Igorek   (2002-10-25 10:46:00) [46]

Просто поднимаю ветку. Ну-ну, будут еще мнения?


Igorek   (2002-10-24 14:08:00) [47]


> Skier © (24.10.02 13:51)
> >Igorek
> Вообще-то вот это очень нехорошая штука.
> НЕТ ВЫЗОВА ДЕСТРУКТОРА КЛАССА-ПРЕДКА TObject
> С этого стоит начать копать дальше...
>
> destructor A.Destroy;
> begin
> f;
> end;
>
> И кроме того Компилятор обязательно выдаст Warning на эту
> строку :
> destructor Destroy; virtual;

Вы правы в обеих случаях, но это не важно для данного случая. Написал так, с ходу.


> А вызывать виртуальные функции из деструктора конечно можно.
> Деструктор в OP - не совсем то же, что и в С++. Воспринимай
> его как событие OnDestructing (но не OnDestructed) :)

Недопонял. Событие OnDestructing (BeginDestructing) должно выставить флажок внутри класса (UnderDestructing)?


VaS   (2002-10-24 13:52:00) [48]

Во-первых:

A = class
procedure f; virtual;
destructor Destroy; override;
end;


А вызывать виртуальные функции из деструктора конечно можно. Деструктор в OP - не совсем то же, что и в С++. Воспринимай его как событие OnDestructing (но не OnDestructed) :)


Skier   (2002-10-24 13:51:00) [49]

>Igorek
Вообще-то вот это очень нехорошая штука.
НЕТ ВЫЗОВА ДЕСТРУКТОРА КЛАССА-ПРЕДКА TObject
С этого стоит начать копать дальше...

destructor A.Destroy;
begin
f;
end;

И кроме того Компилятор обязательно выдаст Warning на эту
строку :
destructor Destroy; virtual;


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо