Каждый класс должен иметь виртуальный деструктор? - PullRequest
48 голосов
/ 09 декабря 2008

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


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


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

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

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

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

Ответы [ 8 ]

53 голосов
/ 09 декабря 2008

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

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

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

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

28 голосов
/ 09 декабря 2008

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

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

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

8 голосов
/ 09 декабря 2008

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

4 голосов
/ 10 декабря 2008

Проверьте эту статью от Херб Саттер :

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

2 голосов
/ 09 декабря 2008

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

1 голос
/ 13 мая 2010

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

0 голосов
/ 01 января 2016
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;
}
0 голосов
/ 09 декабря 2008

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

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

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