Когда Вы не должны использовать виртуальные деструкторы?

94
задан curiousguy 9 March 2016 в 22:01
поделиться

9 ответов

Нет никакой потребности использовать виртуальный деструктор, когда любой из ниже верен:

  • Никакое намерение получить классы из него
  • Никакое инстанцирование на "куче"
  • Никакое намерение сохранить в указателе суперкласса

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

68
ответ дан sep 24 November 2019 в 06:06
поделиться

Для ответа на вопрос явно, т.е. когда должен, Вы не объявляете виртуальный деструктор.

C++ '98 / '03

Добавление виртуального деструктора могло бы изменить Ваш класс от того, чтобы быть POD (простые данные) * или агрегат к не-POD. Это может мешать Вашему проекту компилировать, если Ваш тип класса совокупный инициализированный где-нибудь.

struct A {
  // virtual ~A ();
  int i;
  int j;
};
void foo () { 
  A a = { 0, 1 };  // Will fail if virtual dtor declared
}

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

void bar (...);
void foo (A & a) { 
  bar (a);  // Undefined behavior if virtual dtor declared
}

[* тип POD является типом, который имеет определенные гарантии о его расположении памяти. В стандарте действительно только говорится, что, если необходимо было скопировать с объекта с типом POD в массив символов (или неподписанных символов) и назад снова, тогда результат совпадет с исходным объектом.]

современный C++

В последних версиях C++, понятие POD было разделено между расположением класса и его конструкцией, копированием и разрушением.

Для случая замещающего знака, это больше не неопределенное поведение, это теперь условно поддерживается с определенной реализацией семантикой (N3937 - ~C ++ '14 - 5.2.2/7):

... Передавая потенциально оцененный аргумент типа класса (Пункт 9), имеющий нетривиального конструктора копии, нетривиального конструктора перемещения, или на-тривиальном, деструктор, без соответствующего параметра, условно поддерживается с определенной реализацией семантикой.

Объявление деструктора кроме =default будет означать, что это не тривиально (12.4/5)

... Деструктор тривиален, если он не обеспечивается пользователями...

Другие изменения в современном C++ уменьшают влияние совокупной проблемы инициализации, поскольку может быть добавлен конструктор:

struct A {
  A(int i, int j);
  virtual ~A ();
  int i;

  int j;
};
void foo () { 
  A a = { 0, 1 };  // OK
}
67
ответ дан Donald Duck 24 November 2019 в 06:06
поделиться

Я объявляю виртуальный деструктор, если и только если у меня есть виртуальные методы. Как только у меня есть виртуальные методы, я не доверяю мне, чтобы не инстанцировать его на "куче" или хранить указатель на базовый класс. Оба из них являются чрезвычайно общими операциями и будут часто пропускать ресурсы тихо, если деструктор не объявляется виртуальный.

25
ответ дан Andy 24 November 2019 в 06:06
поделиться

Виртуальный деструктор необходим каждый раз, когда существует любой шанс, который delete можно было бы назвать на указателе на объект подкласса с типом Вашего класса. Это удостоверяется, что корректный деструктор называют во время выполнения без компилятора, имеющего необходимость знать класс объекта на "куче" во время компиляции. Например, примите B, подкласс A:

A *x = new B;
delete x;     // ~B() called, even though x has type A*

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

Однако, если Вы оказались delete луг много объектов в жестком цикле, производительность наверху вызывания виртуальной функции (даже один это пусто) могло бы быть примечательным. Компилятор не может обычно встраивать эти вызовы, и процессору могло бы быть тяжело предсказывать, куда пойти. Маловероятно, что это оказало бы значительное влияние на производительность, но это стоит упомянуть.

6
ответ дан Jay Conrod 24 November 2019 в 06:06
поделиться

Не все классы C++ подходят для использования в качестве базового класса с динамическим полиморфизмом.

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

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

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

class MutexLock {
    mutex *mtx_;
public:
    explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
    ~MutexLock() { mtx_->unlock(); }
private:
    MutexLock(const MutexLock &rhs);
    MutexLock &operator=(const MutexLock &rhs);
};

смысл этого класса должен находиться на стеке для RAII. Если Вы раздаете указатели на объекты этого класса, уже не говоря о подклассах его, то Вы Делаете Его Неправильно.

5
ответ дан Steve Jessop 24 November 2019 в 06:06
поделиться

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

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

Во всех других случаях, Вы сохраните себя страдание отладки для создания dtor виртуального.

5
ответ дан mxcl 24 November 2019 в 06:06
поделиться

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

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

1
ответ дан Jørn Jensen 24 November 2019 в 06:06
поделиться

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

-7
ответ дан Windows programmer 24 November 2019 в 06:06
поделиться

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

3
ответ дан 24 November 2019 в 06:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: