Должен ли каждый класс иметь виртуальный деструктор?

Java и С# поддерживают понятие классов, которые не могут использоваться в качестве базовых классов с ключевыми словами final и sealed. В С++, однако, нет хорошего способа предотвратить вывод из класса, из которого возникает автор класса с дилеммой, должен ли каждый класс иметь виртуальный деструктор или нет?


Изменить: Поскольку С++ 11 это больше не верно, вы можете указать, что класс final.


С одной стороны, предоставление объекта виртуальному деструктору означает, что он будет иметь vtable и, следовательно, потребляет 4 (или 8 на 64-разрядных машинах) дополнительные байты для объекта для vptr.

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

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

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

Ответы

Ответ 1

Вопрос в том, действительно ли вы хотите применять правила о том, как использовать ваши классы? Зачем? Если класс не имеет виртуального деструктора, любой, кто использует класс, знает, что он не предназначен для вывода, и какие ограничения применяются, если вы попробуете его в любом случае. Разве это недостаточно хорошо?

Или вам нужен компилятор, чтобы бросить жесткую ошибку, если кто-то посмеет сделать то, чего вы не ожидали?

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

Ответ 2

Каждый абстрактный класс должен либо иметь a,

  • защищенный деструктор, или
  • виртуальный деструктор.

Если у вас есть общедоступный не виртуальный деструктор, это не хорошо, поскольку он позволяет пользователям удалять через этот указатель производный объект. Поскольку, как мы все знаем, это поведение undefined.

Для класса, не предназначенного для удаления с помощью указателя на него, нет никакой причины иметь виртуальный деструктор. Это будет не только отходы ресурсов, но, что более важно, это даст пользователям неправильный намек. Просто подумайте о том, какой дрянной смысл он сделает, чтобы дать std::iterator виртуальный деструктор.

Ответ 3

Нет! Виртуальные деструкторы используются только тогда, когда объект производного класса удаляется с помощью указателя базового класса. Если ваш класс не предназначен для того, чтобы служить базой в этом сценарии, не делайте виртуальный деструктор - вы отправляете неправильное сообщение.

Ответ 4

Отметьте эту статью из Herb Sutter:

Руководство № 4: деструктор базового класса должен быть открытым или виртуальным, или защищенным и не виртуальным.

Ответ 5

Я бы не ответил на общий вопрос. Не каждый класс нуждается в этом. Если вы можете знать, что класс никогда не должен унаследован, тогда нет необходимости брать накладные расходы. Но если есть шанс, будьте в безопасности и поместите туда.

Ответ 6

Я добавлю, что были времена, когда я немного поцарапал себе голову над деструкторами, которые не вызываются, когда я забыл о виртуальном в родительском или дочернем классе. Наверное, я знаю, что сейчас это ищу.:)

Кто-то может возразить, что иногда родительский класс делает что-то в своем деструкторе, что ребенок не должен делать... но это, вероятно, индикатор чего-то не так с вашей структурой наследования в любом случае.

Ответ 7

Базовый класс становится абстрактным классом, когда он содержит хотя бы одну чистую виртуальную функцию. Если у Base нет виртуального деструктора и Derived (производный от Base), то вы можете безопасно уничтожить объект Derived с помощью указателя Derived объекта, но не через указатель базового объекта.

Ответ 8

include<iostream> using namespace std;

class base {
    public: base() {
        cout << "In base class constructor" << endl;
    }

    virtual ~base() {
        cout << "in base class destuctor" << endl;
    }
};

class derived : public base {
    public: derived() {
        cout << "in derived class constructor" << endl;
    }

    ~derived() {
        cout << "in derived class destructor" << endl;
    }
};

int main() {
    base *b; // pointer to the base
    class b = new derived; // creating the derived class object using new
    keyword;
    delete b;
    return 0;
}