Как должно переопределять удаление в C++?
Проблема, с которой я сталкиваюсь, состоит в том, что, насколько я знаю, оператор удаления должен быть статической функцией, но иногда компилятор (VC++), кажется, рассматривает его как динамический.
Дано:
class Base
{
public:
void* operator new(size_t size) { /* allocate from custom heap */ }
void operator delete(void *p) { customFree(p, sizeof(Base)); }
Base() {}
virtual ~Base() {}
};
class Derived: public Base
{
public:
void* operator new(size_t size) { /* allocate from custom heap */ }
void operator delete(void *p) { customFree(p, sizeof(Derived)); }
Derived() {}
virtual ~Derived() {}
}
Я вижу, что удаление базового указателя приведет к вызову Derived::opeator
удалять.
Base *p = new Derived();
delete p; //calls Derived::operator delete
Если я не определяю ЛЮБЫЕ деструкторы, я получаю то, что ожидал: вызывается Base::operator delete. Похоже, это происходит потому, что компилятор вставляет функцию под названием "скалярное удаление деструктора" в виртуальную таблицу, когда деструктор определен. Затем эта функция будет вызывать Derived::delete
,
Поэтому у меня есть вопросы: 1) Это стандартное поведение? 2) Когда я должен использовать
void operator delete( void *, size_t );
против
void operator delete( void * );
если выше стандартное поведение?
2 ответа
Это, безусловно, стандартное поведение. Если был использован оператор производного класса new, также будет использован его оператор delete (также обратите внимание, что даже если вы явно не сообщаете компилятору, что функции являются статическими, они неявно объявляются так). Может быть непростой случай, когда у вас есть оператор new в производном классе, но соответствующий оператор delete находится в базовом классе. Я думаю, что это действительно так, но я бы избежал этого. Полагаясь на базовый оператор delete, при определении собственного оператора новый в производном классе неизбежно вызовет проблемы.
Если я не определяю ЛЮБЫЕ деструкторы, я получаю то, что ожидал:
Вы получите неопределенное поведение:) Все может случиться, включая то, что вы ожидаете (неправильно). Удаление через базовый указатель, который указывает на объект другого типа, требует виртуального деструктора. Неявно объявленный деструктор не является виртуальным.
Когда я должен использовать оператор void delete( void *, size_t);
Если вы хотите, чтобы размер был выделен известным в операторе delete. Я писал о том, что это значит здесь: что делает новый оператор C++, кроме распределения и вызова ctor?, Если вы используете (из вашего перегруженного оператора-члена delete / new) глобальный оператор new & delete, чтобы получить свою память и освободить ее, или даже malloc / free, вам не нужна эта информация о размере. Но это может быть полезно для целей регистрации.
(эй, я должен опубликовать сначала и посмотреть позже:))
Вот соответствующие выдержки из Стандарта:
1 Оператор delete-expression уничтожает наиболее производный объект (intro.object) или массив, созданный новым выражением. delete-expression:::opt delete cast-expression::opt delete [ ] cast-expression Первая альтернатива предназначена для объектов, не являющихся массивами, а вторая - для массивов. Операнд должен иметь тип указателя или тип класса, имеющий единственную функцию преобразования (class.conv.fct) в тип указателя. Результат имеет тип void.
3 В первом альтернативном варианте (удаление объекта), если статический тип операнда отличается от его динамического типа, статический тип должен быть базовым классом динамического типа операнда, а статический тип должен иметь виртуальный деструктор, или поведение не определено. Во втором варианте (удаление массива), если динамический тип удаляемого объекта отличается от его статического типа, поведение не определено.19)