Java и C# поддерживают понятие классов, которые нельзя использовать в качестве базовых классов с ключевыми словами final и sealed. В C++, однако, нет хорошего способа предотвратить создание класса, что ставит автора класса перед дилеммой: должен ли каждый класс иметь виртуальный деструктор или нет?
Редактировать: Поскольку C++ 11 больше не соответствует действительности, вы можете указать, что класс - это final.
С одной стороны, предоставление объекту виртуального деструктора означает, что он будет иметь vtable и, следовательно, потреблять 4 (или 8 на 64-битных машинах) дополнительных байтов на объект для vptr.
С другой стороны, если кто-то позже будет производным от этого класса и удалит производный класс с помощью указателя на базовый класс, программа будет некорректно определена (из-за отсутствия виртуального деструктора), и откровенно оптимизация указателя для каждого объекта будет смешной.
На захватывающая рука наличие виртуального деструктора (возможно) объявляет, что этот тип предназначен для использования полиморфно.
Некоторые люди думают, что вам нужна явная причина не использовать виртуальный деструктор (как подтекст этот вопрос), а другие говорят, что вы должны использовать их только тогда, когда у вас есть причина полагать, что ваш класс должен быть производным от, что думает ты ?
Дубликаты: stackoverflow.com/questions/270917/…, stackoverflow.com/questions/300986/…
«и, откровенно говоря, оптимизировать указатель на объект - это просто смешно». Это не смешно для небольших объектов. C++ 0x добавляет контейнер forward_list именно потому, что иногда накладные расходы на один указатель на объект слишком велики - из-за требований к пространству и времени.
@onebyone, этот вопрос не является дубликатом первого вопроса, который вы перечисляете, который специфичен для классов абстрактный, и я ссылаюсь на второй в своем вопросе, я не думаю, что это дубликат, потому что вопрос сильно предвзято относится к vritual dtors и я хотел открытого обсуждения.
@Kyralessa, компьютерщик должен делать то, что должен делать компьютерщик :)
Отвечает ли это на ваш вопрос? Зачем мне объявлять виртуальный деструктор абстрактного класса в C++?





Я бы ответил «нет» на общий вопрос. Он не нужен классу каждый. Если вы знаете, что класс никогда не должен наследоваться, тогда нет необходимости нести незначительные накладные расходы. Но если есть шанс, будьте осторожны и положите его туда.
Нет! Виртуальные деструкторы используются только тогда, когда объект производного класса удаляется с помощью указателя базового класса. Если ваш класс не предназначен для использования в качестве основы в этом сценарии, не делайте деструктор виртуальным - вы отправите неправильное сообщение.
Итак, как вы предвидите каждый случай, когда ваш код будет полезен? Возможно, будет какой-то случай, когда ваш класс, который предназначен для использования вами неполиморфно, будет полезен только как базовый класс в каком-то очень конкретном экземпляре.
Каждый абстрактный класс должен иметь либо,
Если у вас есть общедоступный не виртуальный деструктор, это бесполезно, поскольку он позволяет пользователям удалять с помощью этого указателя производный объект. Поскольку, как мы все знаем, это неопределенное поведение.
Для абстрактного класса вам уже нужен указатель виртуальной таблицы в объекте, поэтому создание деструктора virtual не имеет (насколько мне известно) высоких затрат с точки зрения пространства или производительности во время выполнения. И это имеет то преимущество, что производные классы автоматически имеют свои деструкторы virtual (см. Комментарий @Aconcagua). Конечно, вы также можете сделать деструктор protected virtual для этого случая.
Я не думаю, что для неабстрактного класса, не предназначенного для удаления с помощью указателя на него, есть веская причина для создания виртуального деструктора. Это приведет к потере ресурсов, но, что более важно, даст пользователям неправильную подсказку. Только подумайте, какой странный смысл было бы дать std::iterator виртуальный деструктор.
Или struct tm из <ctime>, который перестанет быть POD и, следовательно, больше не будет совместим с соглашениями о вызовах C.
отличный ответ, литб. Я собираюсь изменить весь свой код прямо сейчас.
Позднее добавление, я знаю, но я не согласен с защищенным деструктором. Кто-то может упустить из виду необходимость снова сделать общедоступный деструктор производного класса виртуальным, а кто-то другой может получить производный деструктор, предполагая уже виртуальный деструктор - и (незаконно) удалить внучатого ребенка через указатель на дочерний ... Конечно, второе лицо тогда произошла ошибка, но ее можно было бы избежать, если бы деструктор уже был виртуальным в самом первом базовом классе, поэтому с точки зрения безопасности я считаю допустимым только второй вариант (конечно, только абстрактные классы).
@Aconcagua Мое правило для второго автора: каждый неабстрактный инстанцируемый класс должен иметь либо виртуальный публичный деструктор, либо невиртуальный публичный деструктор и объявленный final.
Однако ваша идея дать каждому абстрактному классу виртуальный деструктор звучит интересно. Может мне стоит принять это. Мой пример с std::iterator несколько неудачен, потому что std::iterator совсем не абстрактный. Я поправлю.
Я добавлю, что были времена, когда я некоторое время почесал в затылке, что деструкторы не вызываются, когда я забывал виртуальный объект в родительском или дочернем классе. Думаю, теперь я знаю, что нужно искать это. :)
Кто-то может возразить, что бывают случаи, когда родительский класс делает что-то в своем деструкторе, чего не должен делать дочерний ... но это, вероятно, в любом случае является индикатором того, что с вашей структурой наследования что-то не так.
Вопрос в том, действительно ли вы хотите, чтобы правила принуждать о том, как следует использовать ваши классы? Почему? Если у класса нет виртуального деструктора, любой, кто использует класс, знает, что он не предназначен для наследования, и какие ограничения применяются, если вы все равно попробуете его. Разве этого не достаточно?
Или вам нужно, чтобы компилятор выдал серьезную ошибку, если кто-то смеет сделает что-то, чего вы не ожидали?
Дайте классу виртуальный деструктор, если вы хотите, чтобы люди наследовали его. В противном случае не делайте этого и предполагайте, что любой, кто использует ваш код, достаточно умен, чтобы правильно использовать ваш код.
@jalf: если они происходят от него и хотят использовать его полиморфным способом /
Проверьте эта статья из Herb Sutter:
Рекомендация №4: деструктор базового класса должен быть либо общедоступным и виртуальным, либо защищенным и невиртуальным.
Обратите внимание, что он говорит о классах, которые должны быть классами основание, этот вопрос конкретно касается классов, не предназначенных для использования в качестве базовых.
Базовый класс становится абстрактным, если он содержит хотя бы одну чистую виртуальную функцию. Если у Base нет виртуального деструктора, а у Derived (производного от Base) есть, то вы можете безопасно уничтожить производный объект с помощью указателя на производный объект, но не с помощью указателя на объект Base.
Уже есть вопросы о «за» и «против» - это дубликат или это опрос общественного мнения? В последнем случае, может быть, вам стоит создать для голосования ответы «да» и «нет», а затем закрыть вопрос? Я думаю, что это рекомендуемый способ реализации опроса с несколькими вариантами ответов на SO.