размещение новых и удаление - PullRequest
44 голосов
/ 22 июля 2011

Какой правильный способ удалить всю выделенную здесь память?

  const char* charString = "Hello, World";
  void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
  Buffer* buf = new(mem) Buffer(strlen(charString));

  delete (char*)buf;

ИЛИ

  const char* charString = "Hello, World";
  void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
  Buffer* buf = new(mem) Buffer(strlen(charString));

  delete buf;

или они оба одинаковые?

Ответы [ 3 ]

47 голосов
/ 22 июля 2011

Правильный метод:

buf->~Buffer();
::operator delete(mem);

Удалить можно только с помощью оператора delete то, что вы получили от оператора new .Если вы напрямую вызываете функцию operator new, вы также должны напрямую вызывать функцию operator delete и вручную вызывать деструктор.

25 голосов
/ 22 июля 2011

В C ++ есть два отдельных понятия:

  1. Новые / удалить операторы .

  2. Новый / Удалить выражения .

Операторы выделяют и освобождают память. Выражение new создает объекты. Выражение delete иногда разрушает объект и вызывает оператор.

Почему "иногда"? Потому что это зависит от выражения. Голый, глобальный new сначала вызывает operator-new для выделения памяти, а затем создает объект; глобальный delete вызывает деструктор и освобождает память. Но все другие перегрузки new и delete различны:

  • Перегруженное новое выражение вызывает перегруженный новый оператор для выделения памяти и затем приступает к построению объекта.
  • Однако перегруженного выражения удаления не существует, в частности нет «размещения-удаления»: вместо этого вы должны вызвать деструктор вручную.

Операторы New / Delete все еще должны быть перегружены в совпадающих парах, потому что соответствующий оператор удаления вызывается, когда конструктор объекта выдает исключение. Однако не существует автоматического способа вызова деструктора для объекта, который был выделен перегруженным оператором new, поэтому вы должны сделать это самостоятельно.

В качестве первого и самого основного примера рассмотрим новый оператор размещения , которому поручено принять форму void * operator new (size_t, void * p) throw() { return p; }. Таким образом, соответствующий оператор delete обязан ничего не делать: void operator delete (void *, void *) throw() { }. Использование:

void * p = ::operator new(5); // allocate only!
T * q = new (p) T();          // construct
q->~T();                      // deconstruct: YOUR responsibility
// delete (p) q;   <-- does not exist!! It would invoke the following line:
::operator delete(p, q);      // does nothing!
::operator delete(q);         // deallocate
2 голосов
/ 03 июня 2013

Если предположить, что Buffer::operator delete не существует, верная версия delete buf; выполнит всю необходимую очистку. Чтобы быть немного безопаснее, вы можете сказать ::delete buf;.

Ниже следует материал для обсуждения языка адвоката.

5.3.5 / 1

Оператор delete-expression уничтожает наиболее производный объект (1.8) или массив, созданный с помощью new-expression .

Ненужное-выражение:

  • :: opt delete cast-expression
  • :: opt delete [ ] cast-expression

Первый вариант - для объектов, не являющихся массивами, а второй - для массивов. ...

5.3.5 / 2

... В первой альтернативе ( удалить объект ) значение операнда delete может быть нулевым указателем, указателем на объект, не являющийся массивом, созданный предыдущим new-expression , или указатель на подобъект (1.8), представляющий базовый класс такого объекта (раздел 10). Если нет, поведение не определено.

Таким образом, указатель должен указывать на объект, созданный с помощью new-expression , которое определено:

5.3.4 / 1

новое выражение:

  • :: opt new новое размещение opt новый тип -id _new-initializer_ opt
  • :: opt new новое размещение opt ( идентификатор типа ) новый инициализатор opt

новое размещение:

  • ( список выражений )

Таким образом, "размещение нового" считается новым выражением . Ничто не запрещает delete-expression там.

Кроме того, получается, что delete-expression делает абсолютно правильные вещи для очистки объекта, несмотря на пользовательское создание.

* +1136 * 5.3.5 / 6-9 * * 1 137

Если значение операнда delete-expression не является нулевым значением указателя, delete-expression вызовет деструктор (если таковой имеется) для объекта или элементы массива удаляются. ...

Если значение операнда выражения удаления не является нулевым значением указателя, delete-выражение вызовет функцию освобождения (3.7 .4.2). В противном случае не определено, будет ли вызвана функция освобождения. [ Примечание: Функция освобождения вызывается независимо от того, выбрасывает ли деструктор для объекта или некоторого элемента массива исключение. - конечная нота ]

Когда ключевому слову delete в delete-expression предшествует унарный оператор ::, для освобождения хранилища используется глобальная функция освобождения.

Итак, ::delete buf; полностью эквивалентно:

try {
    buf->~Buffer();
} catch(...) {
    ::operator delete(mem);
    throw;
}
...