![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Стиль: Вызов виртуальных методов из деструкторов
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;