Как должно переопределять удаление в C ++? - PullRequest
9 голосов
/ 03 января 2009

Проблема, с которой я сталкиваюсь, состоит в том, что, насколько я знаю, оператор удаления должен быть статической функцией, но иногда компилятор (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 delete.

Base *p = new Derived();
delete p; //calls Derived::operator delete

Если я не определяю ЛЮБЫХ деструкторов , тогда я получаю то, что ожидал: вызывается Base :: operator delete. Кажется, это происходит потому, что компилятор вставляет функцию под названием «скалярное удаление деструктор в vtable , когда определен деструктор . Тогда эта функция вызовет Derived::delete.

Итак, у меня есть вопросы: 1) Это стандартное поведение? 2) Когда я должен использовать

void operator delete( void *, size_t );

против

void operator delete( void * );

если вышеприведенное является стандартным поведением?

Ответы [ 2 ]

8 голосов
/ 03 января 2009

Это, безусловно, стандартное поведение. Если был использован оператор производного класса new, его оператор удаления также будет использоваться (также обратите внимание, что даже если вы явно не сообщаете компилятору, что функции являются статическими, они являются , неявно объявленными так ). Может быть непростой случай, когда у вас есть оператор new в производном классе, но соответствующий оператор delete находится в базовом классе. Я думаю, что это действительно так, но я бы избежал этого. Полагаясь на базовый оператор delete, при определении собственного оператора новый в производном классе неизбежно вызовет проблемы.

Если я не определяю ЛЮБЫЕ деструкторы, я получаю то, что ожидал:

Вы получите неопределенное поведение :) Все может случиться, включая то, что вы ожидаете (неправильно). Удаление через базовый указатель, который указывает на объект другого типа, требует виртуального деструктора. Неявно объявленный деструктор не является виртуальным.

Когда я должен использовать оператор void delete (void *, size_t);

Если вы хотите, чтобы размер был выделен известным в операторе delete. Я написал о том, что это значит здесь: Что делает новый оператор C ++, кроме выделения и вызова ctor? . Если вы используете (из вашего перегруженного оператора-члена delete / new) глобальный оператор new & delete, чтобы получить свою память и освободить ее, или даже malloc / free, вам не нужна эта информация о размере. Но это может быть полезно для целей регистрации.

6 голосов
/ 03 января 2009

(эй, я должен опубликовать сначала и посмотреть позже :))

Вот соответствующие выдержки из Стандарта:

1 Оператор delete-expression уничтожает самый производный объект ( intro.object ) или массив, созданный выражением new. удалить-выражение: :: opt delete cast-выражение :: opt delete [] cast-выражение Первая альтернатива для объектов, не являющихся массивами, а вторая для массивы. Операнд должен иметь тип указателя или тип класса, имеющий одиночная функция преобразования ( class.conv.fct ) в тип указателя. Результат имеет тип void.

3 В первом варианте (удалить объект), если статический тип операнд отличается от своего динамического типа, статический тип должен быть Базовый класс динамического типа операнда и статического типа должен есть виртуальный деструктор или поведение не определено. Во втором альтернатива (удаление массива), если динамический тип объекта удаленный отличается от своего статического типа, поведение не определено.19)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...