Виртуальный деструктор в темплейтном классе

Модераторы: Romeo, Hawk, Absurd, WinMain, DeeJayC

Ответить
Sera
Сообщения: 38
Зарегистрирован: 03 мар 2005, 17:23
Откуда: Симферополь

Виртуальный деструктор в темплейтном классе

Сообщение Sera » 12 июл 2005, 12:04

В темплейтном классе виртуальная функция правильно перегружается, однако это неверно для виртуального деструктора. Почему?

Код: Выделить всё

#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]

Eugie
Сообщения: 707
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Сообщение Eugie » 12 июл 2005, 14:12

У тебя в производном классе не объявлен деструктор => будет вызван только деструктор базового класса. Вообще, нормальная практика для нетривиальной иерахии классов - объявлять деструкторы виртуальными во всех классах.

Sera
Сообщения: 38
Зарегистрирован: 03 мар 2005, 17:23
Откуда: Симферополь

Сообщение Sera » 12 июл 2005, 15:05

Eugie писал(а):Вообще, нормальная практика для нетривиальной иерахии классов - объявлять деструкторы виртуальными во всех классах.
Одно из правил С++ -- "виртуальный однажды - виртуальный всегда". То есть определив один раз функцию как виртуальную, в наследниках можно:
1) не использовать ключевое слово "virtual" при объявлении этой функции]У тебя в производном классе не объявлен деструктор => будет вызван только деструктор базового класса.[/quote]

Это само собой, Eugie. Однако в том-то и проблема. Деструктор объявлен как виртуальный. Значит, при его вызове должна использоваться функция класса-наследника, то есть S::b_func. Однако вызывается My::b_func.
Это-то и ставит меня в тупик.
(для примера приведена функция call, которая ведет себя по законам виртуальности).

Sera
Сообщения: 38
Зарегистрирован: 03 мар 2005, 17:23
Откуда: Симферополь

Сообщение Sera » 12 июл 2005, 15:56

Нашла, в чем дело.
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.

Да, такие тонкости нужно иметь в виду. Теперь буду знать.

Eugie
Сообщения: 707
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Сообщение Eugie » 12 июл 2005, 16:04

Одно из правил С++ -- "виртуальный однажды - виртуальный всегда"
Безусловно. Собственно, речь шла о том, что если в базовом классе объявлен виртуальный деструктор, хорошим правилом является ЯВНО объявлять таковой и во всех производных классах (слово virtual при этом можно опустить). Т.е. не стоит полагаться на компилятор, т.к. виртуальные деструкторы, в отличие от обычных виртуальных функций, не распространяются автоматически на производные классы, если явно не реализованы в них. Компилятор в таком случае сгенерит деструктор по умолчанию, который вызовет деструктор базового класса, но все виртуальные функции в нем будут вызваны, когда "производная" часть объекта уже как бы не существует, т.е. будет вызвана именно реализация из базового класса. Это особенность именно виртуальных деструкторов.

См.также http://www.codersource.net/cpp_virtual_destructors.html

Eugie
Сообщения: 707
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Сообщение Eugie » 12 июл 2005, 16:08

Ну вот, пока писал, уже сама нашла :)

Sera
Сообщения: 38
Зарегистрирован: 03 мар 2005, 17:23
Откуда: Симферополь

Сообщение Sera » 12 июл 2005, 17:12

:) ) Да, все нашлось. Спасибо за ответ.


Хотя я не согласна с выводом. ЯВНОЕ объявление деструктора во всех производных классах совсем не вылечивает, если не знаешь то, что из деструктора нельзя вызывать виртуальные фунции (замечу, что в некоторых компиляторах это запрещено совсем).
Вот хороший пример:

Код: Выделить всё

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 ) . Вот где ошибка! И самые хорошие правила тут не помогут.

Death Mokar
Сообщения: 3
Зарегистрирован: 07 июл 2005, 10:26

Сообщение Death Mokar » 14 июл 2005, 16:45

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.

Да, такие тонкости нужно иметь в виду. Теперь буду знать.

...у Страуструпа об этом написано. Вообще он указывает на очень многие тонкости, только у него это происходит как правило "по тексту", поэтому внимание не акцентируется.

Death Mokar
Сообщения: 3
Зарегистрирован: 07 июл 2005, 10:26

Сообщение Death Mokar » 14 июл 2005, 17:36

А смысл (по простому) таков: компилятор во время компиляции должен "знать", посредством конструктора/деструктора, КАК создавать объект конкретного класса, а если включить в конструктор/деструктор вызов виртуальной функции, то компилятор лишается такой возможности, поскольку вызов виртуальной функции разрешается (через таблицу вирт.функций) во время выполнения.

Ответить