Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 41
Практический вывод из всего вышесказанного состоит в том, что необоснованно объявлять все деструкторы виртуальными так же неверно, как не объявлять их виртуальными никогда. Можно высказать этот совет и в таком виде: деструкторы следует объявлять виртуальными тогда, когда в классе есть хотя бы одна виртуальная функция.
Однако невиртуальные деструкторы могут стать причиной неприятностей даже при полном отсутствии в классе виртуальных функций. Например, в стандартном классе string нет виртуальных функций, но программисты временами все же используют его как базовый класс:
>class SpecialString: public std::string { // плохо! std::string содержит
>... // невиртуальный деструктор
>};
На первый взгляд такой код может показаться безвредным, но если где-то в приложении вы преобразуете указатель на SpecialString в указатель на string, а затем выполните для этого указателя delete, то немедленно попадете в область неопределенного поведения:
>SpecialString *pss = new SpecialString(“Надвигающаяся опасность”);
>std::string *ps;
>...
>ps = pss; // SpecialString*=>std::string*
>...
>delete ps; // неопределенность! На практике ресурсы, выделенные
>// объекту SpecialString, не будут освобождены, потому
>// что деструктор SpecialString не вызывается
То же относится к любому классу, в котором нет виртуального деструктора, в частности ко всем типам STL-контейнеров (например, vector, list, set, tr1::unordered_map [см. правило 54] и т. д.). Если у вас когда-нибудь возникнет соблазн унаследовать стандартному контейнеру или любому другому классу с невиртуальным деструктором, воздержитесь! (К сожалению, в C++ не предусмотрено никакого механизма предотвращения наследования, как, скажем, final в языке Java, или sealed в C#).
Иногда может быть удобно добавить в класс чисто виртуальный деструктор. Вспомним, что чисто виртуальные функции порождают абстрактные классы, то есть классы, экземпляры которых создать нельзя. Иногда, однако, у вас есть класс, который вы хотели бы сделать абстрактным, но в нем нет ни одной пустой виртуальной функции. Что делать? Поскольку абстрактный класс предназначен для использования в качестве базового и поскольку базовый класс должен иметь виртуальный деструктор, а чисто виртуальная функция порождает абстрактный класс, то решение очевидно: объявить чисто виртуальный деструктор в классе, который вы хотите сделать абстрактным. Вот пример:
>class AWOV { // AWOV = “Abstract w/o Virtuals”
>public: