Как должно быть отменено удаление в С++?
Проблема, с которой я сталкиваюсь, заключается в том, что насколько я знаю, оператор delete должен быть статической функцией, но иногда компилятор (VС++), кажется, рассматривает его как динамический.
Дано:
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
delete.
Base *p = new Derived();
delete p; //calls Derived::operator delete
Если я не определяю ANY деструкторов, тогда я получаю то, что, как я ожидал, вызывается: вызывается метод Base:: operator delete. Кажется, это происходит потому, что компилятор вводит функцию, называемую "скалярное удаление деструктора) в vtable, когда определен деструктор. Затем эта функция вызовет Derived::delete
.
Поэтому у меня есть вопросы:
1) Это стандартное поведение?
2) Когда я должен использовать
void operator delete( void *, size_t );
против.
void operator delete( void * );
если выше стандартное поведение?
Ответы
Ответ 1
Это, безусловно, стандартное поведение. Если использовался оператор производного класса new, также будет использован его оператор delete (также обратите внимание, хотя вы явно не указываете компилятору, что эти функции являются статическими, они неявно объявлены). Там может быть непослушный случай, когда у вас есть оператор new в производном классе, но соответствующий оператор delete находится в базовом классе. Я считаю, что это действительно так, но я бы избегал этого. Опираясь на базовый оператор delete, при определении собственного оператора новый в производном классе неизбежно вызовет проблемы.
Если я не определяю ЛЮБЫХ деструкторов, я получаю то, что, как я ожидал, произойдет:
Вы получите поведение undefined:) Все может произойти, включая то, что вы ожидаете (ошибочно). Удаление с помощью базового указателя, указывающего на объект другого типа, требует виртуального деструктора. Неявно объявленный деструктор не является виртуальным.
Когда я должен использовать удаление void operator (void *, size_t);
Если вы хотите иметь размер, который был выделен известным в операторе delete. Я писал о том, что это означает:
Что делает новый оператор С++ помимо распределения и вызов ctor?. Если вы используете (изнутри вашего перегруженного оператора-члена delete/new) глобальный оператор new и delete, чтобы получить вашу память и освободить ее, или даже malloc/free, вам не нужна эта информация о размере. Но это может быть полезно для ведения журнала.
Ответ 2
(эй, я должен сначала написать и посмотреть позже:))
Вот соответствующие выдержки из Стандарта:
1 Оператор delete-expression уничтожает самый производный объект(intro.object) или массив, созданный новым выражением. удалить-выражение: :: opt delete cast-expression :: opt delete [] cast-expression Первый вариант - для объектов без массива, а второй для массивы. Операнд должен иметь тип указателя или тип класса, имеющий одна функция преобразования (class.conv.fct) в тип указателя. Результат имеет тип void.
3 В первом варианте (удалить объект), если статический тип операнд отличается от своего динамического типа, статический тип должен быть базовый класс динамического типа операнда и статический тип имеют виртуальный деструктор или поведение undefined. Во втором альтернативный (удалить массив), если динамический тип объекта удаленный отличается от его статического типа, поведение undefined.19)