Когда и почему деструктор в базовом классе НЕ должен определяться как виртуальный? - PullRequest
10 голосов
/ 10 августа 2011

Этот пример ниже иллюстрирует, как предотвратить копирование производного класса. Он основан на базовом классе, в котором объявлен и конструктор копирования, и оператор присваивания копии private.

class Uncopyable
{
protected:
   // allow construction and destruction of derived objects...
   Uncopyable() {}
   ~Uncopyable() {}

private:
   // but prevent copying...
   Uncopyable(const Uncopyable&);
   Uncopyable& operator=(const Uncopyable&);
};

Мы можем использовать этот класс в сочетании с частным наследованием, чтобы сделать классы не копируемыми:

class derived: private Uncopyable
{...};

Обратите внимание, что деструктор в классе Uncopyable не объявлен как virtual.

Ранее я узнал, что

  • Деструкторы в базовых классах должны быть virtual.
  • Нельзя создавать деструкторы в неосновных классах virtual.

В этом примере деструктор для Uncopyable не является virtual, но он наследуется от. Кажется, это идет вразрез с мудростью, которой я научился ранее.

Когда и почему деструктор в базовом классе НЕ должен определяться как virtual?

Ответы [ 5 ]

12 голосов
/ 10 августа 2011

Деструктор базового класса должен быть только virtual, если вы можете попытаться освободить объект производного типа через указатель базового типа.Следовательно, если вы наследуете только от базового класса в частном порядке вместо публично , как в случае с Uncopyable, вам не нужно беспокоиться о добавлении virtual деструктор, потому что при использовании частного наследования вы не можете получить указатель на производный объект и сохранить его в указателе на базовый тип.

Другой пример может быть, если вы будете использовать класс mixin, такой какэто тот, который заставляет класс отслеживать количество распределений объектов, от которых наследуется миксин для получения поведения, но не для полиморфной обработки:

template <typename T> class Counter {
public:
    Counter() { ++numInstances; }
    Counter(const Counter&) { ++numInstances );
    ~Counter() { --numInstances; }

    static unsigned getNumInstances() { return numInstances; }

private:
    static unsigned numInstances;
}
template <typename T> unsigned Counter<T>::numInstances = 0;

В более общем случае при использовании статического полиморфизманужны виртуальные деструкторы, потому что вы никогда не обрабатываете классы полиморфно, используя указатели на базовый тип.Вы используете только указатель на производный тип.

Возможно, есть еще несколько случаев, которые я здесь не охватил, но эти два (частное наследование, классы смешивания и статический полиморфизм) покрывают большую часть пространства, гдевиртуальные деструкторы не требуются.

2 голосов
/ 10 августа 2011

Когда вы проектируете базу не как интерфейс , а как подробности реализации (обратите внимание на private наследование от Uncopyable).

1 голос
/ 10 августа 2011

С технической точки зрения вам не нужно делать ваш декостроитель виртуальным, если вы знаете , никто не удалит его как Uncopyable *, но всегда будет удалять его как подкласс того же самого.

Да, это по сути то, что сказал @templatetypedef, но я собираюсь объяснить это, возможно, более простым способом.

Итак: если люди могут сделать что-то вроде этого:

void aFunction(Uncopyable* obj) {
    delete obj;
}

ТогдаВы должны объявить свой деструктор виртуальным (чтобы убедиться, что потенциальные подклассы получают их вызываемый деструктор .

Однако, если вы знаете, что люди будут удалять подклассы следующим образом:

class Widget : public Uncopyable
{
  ....
};

void aFunction(Widget* obj) {
   delete obj;
}

Тогда вам не нужно делать виртуальный деструктор (так будет называться деструктор подклассов).

По крайней мере, это мое понимание.

0 голосов
/ 10 августа 2011

Общее правило - сделать ваш деструктор общедоступным и виртуальным или защищенным и не виртуальным. В первом случае ваш объект использует разрушаемую полиморфно, и виртуальный деструктор будет делать правильные вещи. Во втором случае он будет уничтожен только как фактический дочерний класс и все равно будет делать правильные вещи.

0 голосов
/ 10 августа 2011

Когда ваши объекты должны быть обычными старыми данными без vtable.Я бы прокомментировал это, если мне это когда-нибудь понадобится, поскольку 99% времени, когда «деструкторы» базового класса отключаются от «виртуального», это просто ошибка, которую кто-то захочет исправить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...