В C ++ существует два понятия delete
: первое - это оператор , объявленный как ::operator delete(void*)
, который в основном только освобождает память и обычно не рассматривается большинством программистов.Другим является выражение для удаления , delete p;
, где p
- это T*
.Выражение вызывает деструктор объекта, на который указывает p
(и затем освобождает память), что является важной языковой особенностью C ++, которая не имеет аналога в C.
Как правило, выпара new
выражений с delete
выражениями и malloc()
вызовами функций с free()
вызовами функций:
T * p = new T; // constructor called!
delete p; // destructor called!
void * x = malloc(5); // just raw memory
free(x); // freed
Расширенная часть (не в ответ на вопрос ОП)
Время жизни динамического объекта в C ++ соответствует следующей общей схеме: выделять, конструировать, уничтожать, освобождать.Стандартное выражение new
выполняет выделение и построение, а стандартное выражение delete
выполняет уничтожение и освобождение.
Вы можете записать процесс вручную:
T * p = (T*)::operator new(sizeof(T)); // allocate raw memory
p = new (p) T; // call the constructor ("placement new")
/*...*/
p->~T(); // destroy the object
::operator delete(p); // deallocate the memory
Фактически, есливы действительно хотели реализовать Baby C ++, вы могли бы определить операторы так же, как malloc
/ free
:
void * operator new(size_t n) { return malloc(n); }
void operator delete(void * p) { free(p); }
Настоящая магия C ++ происходит благодаря new
и delete
выражения : стандартное выражение new
вызывает конструктор (выражение new
является способом only для вызова конструктора в C ++!) После выделения,в то время как стандартное выражение удаления вызывает деструктор перед освобождением.
Почему «стандартное выражение»?Ну, вы также можете определить и перегрузить многие другие версии операторов new
и delete
.Тем не менее, есть важная асимметрия: хотя вы можете использовать пользовательский оператор new
в пользовательском выражении new
(обычно называемом «размещение нового»), эквивалентного выражения «размещение-удаление» не существует.Поэтому всякий раз, когда вы используете пользовательское выражение new
, вы должны вручную вызывать деструктор перед вызовом соответствующего пользовательского оператора удаления :
T * p = new (A, B, C) T; // some custom new expression
// Entirely equivalent version:
T * p = (T*) ::operator new(sizeof(T), A, B, C); // this is your custom overload
T * p = new (p) T; // std. placement-new expression calls constructor
/* ---- later ---- */
p->~T(); // Must destroy manually!
::operator delete(p, A, B, C); // your matching custom overload
Обратите внимание, что пользовательского удаления не существуетвыражение delete (A,B,C) p'
!
Для полноты, оператор стандартного размещения стандарта *1062*, единственной целью которого является вызов конструктора, в соответствии со стандартом должен принять следующую форму:
void * operator new(size_t, void * p) { return p; }
Это соответствует delete
Оператор также обязателен, имя ничего не делать:
void operator delete(void * p, void *) { }
Вы можете увидеть в приведенном выше общем примере, почему это необходимо.
Важно всегда перегружать пользовательские версии new
и delete
парами!Причина в том, что если конструктор объекта завершается неудачно с исключением внутри конструктора, то память освобождается при вызове оператора delete
, который соответствует некорректному выражению new
.
Второе обновление: Чтобы быть безопасным от исключений, мы должны учитывать, что конструктор T
может выдать:
Версия 1:
try {
T * p = new (A, B, C) T;
/* ... */
p->~T();
::operator delete(p, A, B, C); // automatically invoked if T::T() throws!
}
catch(...) { }
Версия 2:
void * addr = ::operator new(sizeof(T), A, B, C);
try {
T * p = new (addr) T; // might throw
/* ... */
p->~T();
// ::operator delete(p, addr); // ditto as in (1), but does nothing
}
catch(...) { }
::operator delete(addr, A, B, C);