кажется мне, что нет... ведь деструктор наследника обязан вызвать деструктор базового класса. А что ему делать, если у базового класса деструктор не определен?
virtual ~Base() = 0 {};
как-то странновато такой вариант выглядит... вроде и чисто виртуальный, и в то же время тело определено.
или вот так, например:
Д>кажется мне, что нет... ведь деструктор наследника обязан вызвать деструктор базового класса. А что ему делать, если у базового класса деструктор не определен?
Не бывает чисто виртуального деструктора. Я не знаю пункта стандарта, но по логике ему нет места в ООП.
Класс объявляет чисто виртуальную функцию, принуждая потомков реализовать эту функцию.
Для деструктора ситуация иная — для любого класса деструктор существует. Или определенный пользователем, или созданный компилятором по умолчанию.
Vi2>Для деструктора ситуация иная — для любого класса деструктор существует. Или определенный пользователем, или созданный компилятором по умолчанию.
здесь вопрос не в наличии деструктора, а в его виртуальности. С другой стороны — зачем определение деструктора абстрактному классу?
такое вот объявление:
virtual ~Base() = 0 {};
нормально проходит. Просто я не встречал в литературе объяснение всего этого, вот и пытаюсь разобраться. Как-то странно это все выглядит.
Д>здесь вопрос не в наличии деструктора, а в его виртуальности. С другой стороны — зачем определение деструктора абстрактному классу?
Д>такое вот объявление:
Д>
Д>virtual ~Base() = 0 {};
Д>
Д>нормально проходит. Просто я не встречал в литературе объяснение всего этого, вот и пытаюсь разобраться. Как-то странно это все выглядит.
Здравствуйте, Vi2, Вы писали:
Vi2>Не бывает чисто виртуального деструктора. Я не знаю пункта стандарта, но по логике ему нет места в ООП.
Бывает. Чисто виртуальный — это НЕ значит "НЕ имеющий реализации". Стандарт разрешает чистовиртуальным методам иметь и определение (реализацию)
Vi2>Класс объявляет чисто виртуальную функцию, принуждая потомков реализовать эту функцию.
Так если в базовом классе обьявлен виртуальный деструктор (неважно pure или нет), то в каждом наследнике обязательно он будет перекрыт. Так что с этим все в порядке. Другое дело, если у базового чистовиртуально деструктора нет определения. Тогда будет ошибка при линковке.
Вижу следующее применение чистовиртуальных деструкторов (с определением). Если надо запретить создание экземпляров базового класса и одновременно не требовать перекрыть ни один метод базового класса (кроме деструктора ни одного чистовирт. метода). Ценность сомнительна, но стандарт так делать разрешает.
Здравствуйте, WeCom, Вы писали:
WC>Здравствуйте, Vi2, Вы писали:
Vi2>>Не бывает чисто виртуального деструктора. Я не знаю пункта стандарта, но по логике ему нет места в ООП. WC>Бывает. Чисто виртуальный — это НЕ значит "НЕ имеющий реализации". Стандарт разрешает чистовиртуальным методам иметь и определение (реализацию)
Vi2>>Класс объявляет чисто виртуальную функцию, принуждая потомков реализовать эту функцию. WC>Так если в базовом классе обьявлен виртуальный деструктор (неважно pure или нет), то в каждом наследнике обязательно он будет перекрыт. Так что с этим все в порядке. Другое дело, если у базового чистовиртуально деструктора нет определения. Тогда будет ошибка при линковке. WC>Вижу следующее применение чистовиртуальных деструкторов (с определением). Если надо запретить создание экземпляров базового класса и одновременно не требовать перекрыть ни один метод базового класса (кроме деструктора ни одного чистовирт. метода). Ценность сомнительна, но стандарт так делать разрешает.
Полностью согласен. А по поводу литературы, то я эту технику встречал в Маерса "Effective C++". Вот выдержка с его статьи:
Finally, it's worth mentioning that it can be convenient to declare pure virtual destructors in some classes. Recall that pure virtual functions result in abstract classes — classes that can't be instantiated (i.e., you can't create objects of that type). Sometimes, however, you have a class that you'd like to be abstract, but you don't happen to have any functions that are pure virtual. What to do? Well, because an abstract class is intended to be used as a base class, and because a base class should have a virtual destructor, and because a pure virtual function yields an abstract class, the solution is simple: declare a pure virtual destructor in the class you want to be abstract. ¤ Item E14, P20
Here's an example: ¤ Item E14, P21
class AWOV { // AWOV = "Abstract w/o
// Virtuals"
public:
virtual ~AWOV() = 0; // declare pure virtual
// destructor
};
This class has a pure virtual function, so it's abstract, and it has a virtual destructor, so you can rest assured that you won't have to worry about the destructor problem. There is one twist, however: you must provide a definition for the pure virtual destructor: ¤ Item E14, P22
AWOV::~AWOV() {} // definition of pure
// virtual destructor
You need this definition, because the way virtual destructors work is that the most derived class's destructor is called first, then the destructor of each base class is called. That means that compilers will generate a call to ~AWOV even though the class is abstract, so you have to be sure to provide a body for the function. If you don't, the linker will complain about a missing symbol, and you'll have to go back and add one. ¤
Здравствуйте, Дарней, Вы писали:
Д>здесь вопрос не в наличии деструктора, а в его виртуальности. С другой стороны — зачем определение деструктора абстрактному классу?
Наоборот, нет никакого смысла абстрактному классу не иметь виртуального деструктора. Особенно, когда абстрактный класс представляет собой чисто "интерфейс", то есть содержит только чистовиртуальные методы. Ведь предполагается, что только в одном месте программы будет создаваться обьект определенного наследника, а во всех остальных работа с этим обьектом будет идти через базовый класс (интерфейс), включая и удаление обьекта. А для того, чтобы через базовый класс нормально удалять наследники, деструктор ДОЛЖЕН быть виртуальным.
12.4/7
A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any
derived class are created in the program, the destructor shall be defined. If a class has a base class with a
virtual destructor, its destructor (whether user or implicitly declared) is virtual.
Т.е. деструктор может быть чисто виртуальным, но если существуют неабстрактные наследники, то жтот днструктор должен быть определен (при этом оставаясь чисто виртуальным)
Здравствуйте, WeCom, Вы писали:
WC>Вижу следующее применение чистовиртуальных деструкторов (с определением). Если надо запретить создание экземпляров базового класса и одновременно не требовать перекрыть ни один метод базового класса (кроме деструктора ни одного чистовирт. метода). Ценность сомнительна, но стандарт так делать разрешает.
Возможно кому-то интересен пример, где мог бы быть применен описанный подход. По моему совсем не скромному мнению, например, деструктор IUnknown'а мог бы вполне быть чистовиртуальным.
Д> кажется мне, что нет... ведь деструктор наследника обязан вызвать деструктор Д> базового класса. А что ему делать, если у базового класса деструктор не определен?
Чисто виртуальные деструкторы бывают. В этом отношении деструкторы ведут себя так же, как и любые другие функции, за упомянутым исключением, что деструктор базового класса вызывается деструктором потомка вне зависимости от желания программиста.
Д>
Д> virtual ~Base() = 0 {};
Д>
Д> как-то странновато такой вариант выглядит...
Этот вариант выглядит, действительно, немного "странновато". Но не потому, что есть определение чисто виртуальной функции, а потому что это определение совмещено с ее объявлением. Текущая версия стандарта подобного не разрешает. Т.е. чисто виртуальные функции можно определять только вне тела класса.
Д> вроде и чисто виртуальный, и в то же время тело определено.
"Чистая виртуальность" и наличие определения вовсе не взаимоисключающие вещи. Чисто виртуальной функцию делают для того, чтобы гарантировать, что (1) она будет обязательно реализована в одном из наследников и что (2) класс будет абстрактным. Чисто виртуальными деструкторы обычно делают именно для (2). Т.е., если, например, в классе больше виртуальных функций нет, но хочется, чтобы класс был абстрактным.
Любая виртуальная функция, в том числе и чисто виртуальная, может быть вызвана невиртуально. Это означает, что при желании чисто виртуальные функции можно определять, но вызвать их можно будет только невиртуально. В случае деструктора этот невиртуальный вызов генерируется компилятором в деструкторах классов-наследников, поэтому даже чисто виртуальные деструкторы обязательно должны быть определены. Например:
class C
{
public:
virtual void f();
virtual void g() = 0;
virtual ~C() = 0;
};
void C::f()
{
}
void C::g()
{
}
void C::~C()
{
}
class D : public C
{
public:
void f();
void g();
~D();
};
void D::f()
{
C::f(); // невиртуальный вызов C::f
}
void D::g()
{
C::g(); // невиртуальный вызов C::g, значит чисто виртуальная C::g должна быть определена
}
void D::~D()
{
// неявный невиртуальный вызов C::~C
}
Posted via RSDN NNTP Server 1.5 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, WeCom, Вы писали:
WC>Возможно кому-то интересен пример, где мог бы быть применен описанный подход. По моему совсем не скромному мнению, например, деструктор IUnknown'а мог бы вполне быть чистовиртуальным.
Это бессмыслено, т.к. наследники IUnknown'а никогда не создаются клиентом по new и попыток удалить их delete'ом тоже не должно быть. Управление lifetime'ом объектов делегировано AddRef/Release.
Деструктор IUnknown, скорее, должен был бы быть protected (хотя само наличие деструктора, вероятно, вызвало бы проблемы в языках, отличных от С++).
Здравствуйте, SchweinDeBurg, Вы писали:
SDB>Формально это возможно — все откомпилируется и слинкуется. А в run-time произойдет ошибка — pure virtual function call.
Ошибки не будет. Пример:
#include <iostream>
struct A
{
virtual ~A() = 0;
};
A::~A()
{
std::cout << "A::~A()\n";
}
struct B : public A
{
~B()
{
std::cout << "B::~B()\n";
}
};
int main()
{
B b;
return 0;
}
Здравствуйте, MaximE, Вы писали:
ME>Это бессмыслено, т.к. наследники IUnknown'а никогда не создаются клиентом по new и попыток удалить их delete'ом тоже не должно быть. Управление lifetime'ом объектов делегировано AddRef/Release.
ME>Деструктор IUnknown, скорее, должен был бы быть protected (хотя само наличие деструктора, вероятно, вызвало бы проблемы в языках, отличных от С++).
Нда, думал об одном, а написал другое. Я прдеставил ситуацию, когда решается реализовать управление временем жизни обьекта через подсчет ссылок и эту функциональность поместить в базовый класс. Т.е. в базовом классе определены (невиртуальные) методы AddRef и Release, внутри Release delete this;. Тогда, то и был бы полезен чистовиртуальный деструктор.
В общем к существующей реализации IUnknown'а это относится постольку-поскольку.
Здравствуйте, SchweinDeBurg, Вы писали:
SDB>Здравствуйте, Дарней, Вы писали:
SDB>Формально это возможно — все откомпилируется и слинкуется. А в run-time произойдет ошибка — pure virtual function call.
Это неправильное мнение. Посмотри, в качестве примера CObject (в .Нет — в новом МФЦ).
Деструктор может быть чисто виртуальным и я думаю можно придумать ситуацию , когда это надо. Главное — должно быть определение.
Of course, the code must be complete enough to compile and link.