Страница 1 из 1
Виртуальный деструктор в темплейтном классе
Добавлено: 12 июл 2005, 13:04
Sera
В темплейтном классе виртуальная функция правильно перегружается, однако это неверно для виртуального деструктора. Почему?
Код: Выделить всё
#include <iostream>
template <typename T>
class My
{
public:
My()
{}
virtual ~My()
{
b_func();
}
virtual int b_func()
{
std::cout << "HI!\n";
return 0;
}
virtual void call()
{
this->b_func();
}
};
template <typename T>
class S : public My<T>
{
public:
virtual int b_func()
{
std::cout << "Bye!\n";
return 0;
}
};
int main()
{
// My<int>* p = new My<int>();
My<int>* p = new S<int>();
p->call();
delete p;
return 0;
}
[/code]
Добавлено: 12 июл 2005, 15:12
Eugie
У тебя в производном классе не объявлен деструктор => будет вызван только деструктор базового класса. Вообще, нормальная практика для нетривиальной иерахии классов - объявлять деструкторы виртуальными во всех классах.
Добавлено: 12 июл 2005, 16:05
Sera
Eugie писал(а):Вообще, нормальная практика для нетривиальной иерахии классов - объявлять деструкторы виртуальными во всех классах.
Одно из правил С++ -- "виртуальный однажды - виртуальный всегда". То есть определив один раз функцию как виртуальную, в наследниках можно:
1) не использовать ключевое слово "virtual" при объявлении этой функции]У тебя в производном классе не объявлен деструктор => будет вызван только деструктор базового класса.[/quote]
Это само собой, Eugie. Однако в том-то и проблема. Деструктор объявлен как виртуальный. Значит, при его вызове должна использоваться функция класса-наследника, то есть S::b_func. Однако вызывается My::b_func.
Это-то и ставит меня в тупик.
(для примера приведена функция call, которая ведет себя по законам виртуальности).
Добавлено: 12 июл 2005, 16:56
Sera
Нашла, в чем дело.
http://www.awprofessional.com/articles/ ... 97656&rl=1
Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor.
Да, такие тонкости нужно иметь в виду. Теперь буду знать.
Добавлено: 12 июл 2005, 17:04
Eugie
Одно из правил С++ -- "виртуальный однажды - виртуальный всегда"
Безусловно. Собственно, речь шла о том, что если в базовом классе объявлен виртуальный деструктор, хорошим правилом является ЯВНО объявлять таковой и во всех производных классах (слово virtual при этом можно опустить). Т.е. не стоит полагаться на компилятор, т.к. виртуальные деструкторы, в отличие от обычных виртуальных функций, не распространяются автоматически на производные классы, если явно не реализованы в них. Компилятор в таком случае сгенерит деструктор по умолчанию, который вызовет деструктор базового класса, но все виртуальные функции в нем будут вызваны, когда "производная" часть объекта уже как бы не существует, т.е. будет вызвана именно реализация из базового класса. Это особенность именно виртуальных деструкторов.
См.также
http://www.codersource.net/cpp_virtual_destructors.html
Добавлено: 12 июл 2005, 17:08
Eugie
Ну вот, пока писал, уже сама нашла
Добавлено: 12 июл 2005, 18:12
Sera
) Да, все нашлось. Спасибо за ответ.
Хотя я не согласна с выводом. ЯВНОЕ объявление деструктора во всех производных классах совсем не вылечивает, если не знаешь то, что из деструктора нельзя вызывать виртуальные фунции (замечу, что в некоторых компиляторах это запрещено совсем).
Вот хороший пример:
Код: Выделить всё
class base_class
{
virtual ~base_class()
{
free();
}
virtual void free()
{
//something
}
};
class child_class : public base_class
{
void free()
{
//высвобождение данных
}
};
Здесь, если бы деструктор вел себя как обыкновенная виртуальная функция, то в каждом классе вызывалась бы своя функция free. Еще сегодня утром я сочла бы глупым добавить деструктор в класс child_class с пустым телом, ведь все равно будет вызван деструктор ~base_class ( и вызовет child_class::free ) . Вот где ошибка! И самые хорошие правила тут не помогут.
Добавлено: 14 июл 2005, 17:45
Death Mokar
Sera писал(а):Нашла, в чем дело.
http://www.awprofessional.com/articles/ ... 97656&rl=1
Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor.
Да, такие тонкости нужно иметь в виду. Теперь буду знать.
...у Страуструпа об этом написано. Вообще он указывает на очень многие тонкости, только у него это происходит как правило "по тексту", поэтому внимание не акцентируется.
Добавлено: 14 июл 2005, 18:36
Death Mokar
А смысл (по простому) таков: компилятор во время компиляции должен "знать", посредством конструктора/деструктора, КАК создавать объект конкретного класса, а если включить в конструктор/деструктор вызов виртуальной функции, то компилятор лишается такой возможности, поскольку вызов виртуальной функции разрешается (через таблицу вирт.функций) во время выполнения.