Подтвердить что ты не робот

Почему деструкторы не являются виртуальными по умолчанию [С++]

Почему С++ не делает деструкторы виртуальными по умолчанию для классов, имеющих хотя бы одну другую виртуальную функцию? В этом случае добавление виртуального деструктора мне ничего не стоит, и не иметь его (почти?) Всегда ошибка. Будет ли С++ 0x адресовать это?

4b9b3361

Ответ 1

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

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

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

Изменить: Позвольте мне вытащить Simon замечательный комментарий здесь: Отметьте этот вопрос SO для кода, созданного для деструкторов. Как вы можете видеть, также необходимо учитывать накладные расходы кода.

Ответ 2

Под буквой стандарта полиморфный класс с не виртуальным деструктором не является ошибкой. Одно конкретное действие, выполняемое на таком объекте, приводит к поведению undefined, но все остальное совершенно кошерное. Поэтому, учитывая мягкое поведение стандарта в отношении того, какие ошибки он позволяет программистам делать, почему деструктору следует уделять особое внимание?

И такое изменение будет иметь затраты, хотя и в основном тривиальные: виртуальная таблица будет на один элемент больше, а виртуальная отправка связана с вызовами деструктора.

Насколько я знаю, нет, нет изменений в поведении деструкторов в этом отношении в С++ 11. Я предполагаю, что он сказал бы что-то в разделе о специальных функциях-членах, но это не так, и в разделе виртуальных функций вообще ничего не происходит.

Ответ 3

Вот пример (не то, что я рекомендую писать такой код):

struct base {
    virtual void foo() const = 0;
    virtual void bar () const = 0;
};

struct derived: base {
    void foo() const {}
    void bar() const {}
};

std::shared_ptr<base>
make_base()
{
    return std::make_shared<derived>();
}

Это совершенно прекрасный код, который не показывает UB. Это возможно, потому что std::shared_ptr использует стирание типа; последний вызов delete удалит a derived*, даже если последний std::shared_ptr для запуска уничтожения имеет тип std::shared_ptr<void>.

Обратите внимание, что это поведение std::shared_ptr не приспособлено к виртуальному уничтожению; он имеет множество других применений (например, std::shared_ptr<FILE> { std::fopen( ... ), std::fclose }). Однако, поскольку этот метод уже оплачивает стоимость какой-либо косвенности для работы, некоторые пользователи могут не заинтересоваться виртуальным деструктором для своих базовых классов. Что означает "платить только за то, что вам нужно".