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

Размещение новых и удаление

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

  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;

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

В С++ есть два разных понятия:

  • Операторы new/delete .

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

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

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

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

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

В качестве первого и самого базового примера рассмотрим оператор размещения-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

Ответ 3

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

Далее следуют материалы дискуссий по вопросам языка и адвоката.

5.3.5/1

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

удалить-выражение:

  • :: optdelete cast-expression
  • :: optdelete [ ] cast-expression

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

5.3.5/2

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

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

5.3.4/1

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

  • :: optnew new-placement opt new-type-id _new-initializer_ opt
  • :: optnew new-placement opt( type-id ) new-initializer opt

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

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

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

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

5.3.5/6-9

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

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

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

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

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