Есть ли вред от виртуального деструктора без виртуального метода?

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

Мой вопрос: есть ли какой-то вред, если я сразу добавлю виртуальный деструктор при создании класса (даже без виртуальных методов)? В основном идея состоит в том, чтобы не забыть его позже. Особенно с n производными классами мне не нужно было бы позже менять его в n местах.


person Horst Walter    schedule 24.03.2013    source источник


Ответы (5)


Размер таблицы виртуальных функций связан с небольшими накладными расходами. Переживать, наверное, не стоит. Виртуальный деструктор также сделает ваш класс неагрегированным, нетривиальным классом, классом с нестандартной компоновкой и, следовательно, классом, отличным от POD. Это может быть нежелательно, в зависимости от проблемы.

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

person Joseph Mansfield    schedule 24.03.2013
comment
Я удивлен, что все здесь говорят о накладных расходах виртуальной таблицы, которые незначительны, но не о том факте, что наличие деструктора virtual делает класс не классом POD, что может быть нежелательно. Напоминает мне похожий вопрос, на который я ответил некоторое время назад. - person Alok Save; 24.03.2013
comment
@AlokSave также делает его неагрегированным, что может быть проблемой. - person juanchopanza; 24.03.2013
comment
@juanchopanza Это тоже добавил. Спасибо вам обоим. - person Joseph Mansfield; 24.03.2013

Нет, имеет смысл иметь виртуальный деструктор, даже если у вас нет других виртуальных методов.

Однако, если использование памяти важно, каждый байт имеет значение, и вы можете получить 4 или 8 байтов, если у вас нет виртуальных методов. В моем приложении у меня есть несколько классов, миллионы экземпляров которых у меня есть. В этом случае избавление от v-указателя в вашем классе действительно имеет смысл.

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

person Patrick    schedule 24.03.2013

Единственный вред заключается в том, что ваш класс и все производные классы будут иметь v-таблицу, которая незначительно увеличится в размере. Вам не нужно будет вносить какие-либо изменения в ваши производные классы, даже если вы решите позже сделать деструктор базового класса виртуальным. Для любого метода, включая деструктор, нужно использовать виртуальное ключевое слово только один раз — в базовом классе. Тот же метод в производном классе автоматически становится виртуальным.

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

class A
{
    protected:
    ~A(){}
};

class B : public A
{};

int main(int argc, char *argv[])
{
    A * p = new B;
    delete p;
}

На моем компиляторе выдает следующую ошибку

ошибка C2248: «A::~A»: невозможно получить доступ к защищенному члену, объявленному в классе «A» a.cpp(9): компилятор сгенерировал здесь «A::~A» a.cpp(6): см. объявление « А'

person user93353    schedule 24.03.2013

Не следуйте слепо правилам. То есть следуйте правилам, но не делайте этого вслепую.

Единственный случай, когда виртуальный деструктор действительно необходим, — это когда объект удаляется через его базовый указатель объекта. Эмпирическое правило обобщает и упрощает это условие: если объект может быть удален с помощью указателя на его базовый объект, то он будет использоваться полиморфно; полиморфный объект, скорее всего, будет иметь виртуальные функции, а объект, имеющий виртуальные функции, скорее всего, будет использоваться полиморфно; поэтому объекту с виртуальными функциями, скорее всего, понадобится виртуальный деструктор.

Это все прекрасно и ладно, правило в основном работает, но есть более важный и более фундаментальный факт, который редко упоминается, отчасти потому, что такие правила действительно работают. Дело в том, что есть ценностные объекты, а есть объекты другого вида, у которого нет хорошего названия, но я буду называть их сущностноподобными объектами. Сущностно-подобные объекты имеют идентичность отдельно от своего значения, они используют семантику ссылок, их нельзя копировать без веских причин (например, для создания отдельной идентичности), к ним, вероятно, будет осуществляться полиморфный доступ и т. д. Ценностно-подобные объекты не имеют идентичности помимо их ценности, их можно свободно копировать, их нельзя использовать полиморфно и т. д. Они настолько разные, что стоило бы иметь разные ключевые слова для их классов! Когда вы разрабатываете свой класс, вы должны решить, к какой категории он принадлежит. Тогда ваш вопрос решается сам собой. Сущности получают виртуальные деструкторы, а значения — нет.

person n. 1.8e9-where's-my-share m.    schedule 24.03.2013

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

person Pete Becker    schedule 24.03.2013