operator delete, operator delete[]
Определён в заголовочном файле <new> | ||
|---|---|---|
| Заменяемые обычные функции освобождения памяти | ||
void operator delete ( void* ptr ) throw(); | (1) | (до C++11) |
void operator delete ( void* ptr ) noexcept; | (начиная с C++11) | |
void operator delete[]( void* ptr ) throw(); | (2) | (до C++11) |
void operator delete[]( void* ptr ) noexcept; | (начиная с C++11) | |
void operator delete ( void* ptr, std::align_val_t al ) noexcept; | (3) | (начиная с C++17) |
void operator delete[]( void* ptr, std::align_val_t al ) noexcept; | (4) | (начиная с C++17) |
void operator delete ( void* ptr, std::size_t sz ) noexcept; | (5) | (начиная с C++14) |
void operator delete[]( void* ptr, std::size_t sz ) noexcept; | (6) | (начиная с C++14) |
void operator delete ( void* ptr, std::size_t sz,
std::align_val_t al ) noexcept;
| (7) | (начиная с C++17) |
void operator delete[]( void* ptr, std::size_t sz,
std::align_val_t al ) noexcept;
| (8) | (начиная с C++17) |
| Заменяемые функции освобождения памяти для размещения | ||
void operator delete ( void* ptr, const std::nothrow_t& tag ) throw(); | (9) | (до C++11) |
void operator delete ( void* ptr, const std::nothrow_t& tag ) noexcept; | (начиная с C++11) | |
void operator delete[]( void* ptr, const std::nothrow_t& tag ) throw(); | (10) | (до C++11) |
void operator delete[]( void* ptr, const std::nothrow_t& tag ) noexcept; | (начиная с C++11) | |
void operator delete ( void* ptr, std::align_val_t al,
const std::nothrow_t& tag ) noexcept;
| (11) | (начиная с C++17) |
void operator delete[]( void* ptr, std::align_val_t al,
const std::nothrow_t& tag ) noexcept;
| (12) | (начиная с C++17) |
| Функции освобождения памяти для размещения без выделения памяти | ||
void operator delete ( void* ptr, void* place ) throw(); | (13) | (до C++11) |
void operator delete ( void* ptr, void* place ) noexcept; | (начиная с C++11) | |
void operator delete[]( void* ptr, void* place ) throw(); | (14) | (до C++11) |
void operator delete[]( void* ptr, void* place ) noexcept; | (начиная с C++11) | |
| Пользовательские функции освобождения памяти для размещения | ||
void operator delete ( void* ptr, args... ); | (15) | |
void operator delete[]( void* ptr, args... ); | (16) | |
| Обычные функции освобождения памяти, специфичные для класса | ||
void T::operator delete ( void* ptr ); | (17) | |
void T::operator delete[]( void* ptr ); | (18) | |
void T::operator delete ( void* ptr, std::align_val_t al ); | (19) | (начиная с C++17) |
void T::operator delete[]( void* ptr, std::align_val_t al ); | (20) | (начиная с C++17) |
void T::operator delete ( void* ptr, std::size_t sz ); | (21) | |
void T::operator delete[]( void* ptr, std::size_t sz ); | (22) | |
void T::operator delete ( void* ptr, std::size_t sz, std::align_val_t al ); | (23) | (начиная с C++17) |
void T::operator delete[]( void* ptr, std::size_t sz, std::align_val_t al ); | (24) | (начиная с C++17) |
| Функции освобождения памяти для размещения, специфичные для класса | ||
void T::operator delete ( void* ptr, args... ); | (25) | |
void T::operator delete[]( void* ptr, args... ); | (26) | |
| Обычные функции освобождения памяти с уничтожением, специфичные для класса | ||
void T::operator delete( T* ptr, std::destroying_delete_t ); | (27) | (начиная с C++20) |
void T::operator delete( T* ptr, std::destroying_delete_t,
std::align_val_t al );
| (28) | (начиная с C++20) |
void T::operator delete( T* ptr, std::destroying_delete_t, std::size_t sz ); | (29) | (начиная с C++20) |
void T::operator delete( T* ptr, std::destroying_delete_t,
std::size_t sz, std::align_val_t al );
| (30) | (начиная с C++20) |
Освобождает память, ранее выделенную соответствующей operator new или operator new[]. Эти функции освобождения памяти вызываются delete и выражениями delete[], а также выражениями размещения new для освобождения памяти после уничтожения объектов (или неудачной попытки их создания) с динамической продолжительностью хранения. Их также можно вызывать с помощью обычного синтаксиса вызова функции.
ptr.ptr.ptr не является нулевым указателем и выполняется одно из следующих условий, поведение не определено: - Для operatordelete значение
ptrне представляет адрес блока памяти, выделенного более ранним вызовом (возможно, заменённого) operatornew(std::size_t) (для перегрузок (1,5,9)) или operatornew(std::size_t,std::align_val_t) (для перегрузок (3,7,11)), который не был признан недействительным промежуточным вызовом operatordelete. - Для operatordelete[] значение
ptrне представляет адрес блока памяти, выделенного более ранним вызовом (возможно, заменённого) operatornew[](std::size_t) (для перегрузок (2,6,10)) или operatornew[](std::size_t,std::align_val_t) (для перегрузок (4,8,12)), который не был признан недействительным промежуточным вызовом operatordelete[].
*ptr перед вызовом operatordelete. Вместо этого за прямой вызов деструктора, например с помощью ptr->~T();, отвечает данный operatordelete.Перегрузки (1-8) неявно объявляются в каждой единице трансляции, даже если заголовок <new> не включён.
Критерии выбора перегрузки см. в разделе выражение delete.
Параметры
| ptr | - | указатель на освобождаемый блок памяти или нулевой указатель |
| sz | - | размер, переданный соответствующей функции выделения памяти |
| place | - | указатель, использованный в качестве параметра размещения в соответствующем размещающем new |
| tag | - | тег для разрешения неоднозначности перегрузки, соответствующий тегу, использованному в операторе new без выбрасывания исключений |
| al | - | выравнивание выделенного объекта или элемента массива |
| args | - | произвольные параметры, соответствующие функции выделения памяти для размещения (могут включать std::size_t и std::align_val_t) |
Исключения
| Все функции освобождения памяти имеют спецификацию noexcept(true), если в объявлении не указано иное. | (начиная с C++11) |
Если функция освобождения памяти завершается выбрасыванием исключения, поведение не определено, даже если она объявлена со спецификацией noexcept(false)(начиная с C++11).
Глобальные замены
Перегрузки (1-12) являются заменяемыми. Реализации по умолчанию действуют следующим образом:
ptr является нулевым, ничего не делает. В противном случае освобождает память, выделенную более ранним вызовом operatornew.operator delete(ptr) так, как если бы перегрузка (1) могла освободить память, выделенную более ранним вызовом operatornew[].operator delete(ptr, al) так, как если бы перегрузка (3) могла освободить память, выделенную более ранним вызовом operatornew[].operator delete(ptr).operator delete[](ptr).operator delete(ptr, al).operator delete[](ptr, al).operator delete(ptr).operator delete[](ptr).operator delete(ptr, al).operator delete[](ptr, al).Замена глобальных operator операторов new/delete:
#include <cstdio>
#include <cstdlib>
#include <new>
// no inline, required by [replacement.functions]/3
void* operator new(std::size_t sz)
{
std::printf("1) new(size_t), size = %zu\n", sz);
if (sz == 0)
++sz; // avoid std::malloc(0) which may return nullptr on success
if (void *ptr = std::malloc(sz))
return ptr;
throw std::bad_alloc{}; // required by [new.delete.single]/3
}
// no inline, required by [replacement.functions]/3
void* operator new[](std::size_t sz)
{
std::printf("2) new[](size_t), size = %zu\n", sz);
if (sz == 0)
++sz; // avoid std::malloc(0) which may return nullptr on success
if (void *ptr = std::malloc(sz))
return ptr;
throw std::bad_alloc{}; // required by [new.delete.single]/3
}
void operator delete(void* ptr) noexcept
{
std::puts("3) delete(void*)");
std::free(ptr);
}
void operator delete(void* ptr, std::size_t size) noexcept
{
std::printf("4) delete(void*, size_t), size = %zu\n", size);
std::free(ptr);
}
void operator delete[](void* ptr) noexcept
{
std::puts("5) delete[](void* ptr)");
std::free(ptr);
}
void operator delete[](void* ptr, std::size_t size) noexcept
{
std::printf("6) delete[](void*, size_t), size = %zu\n", size);
std::free(ptr);
}
int main()
{
int* p1 = new int;
delete p1;
int* p2 = new int[10]; // guaranteed to call the replacement in C++11
delete[] p2;
}
Возможный вывод:
// Compiled with GCC-5 in C++17 mode to obtain the following: 1) op new(size_t), size = 4 4) op delete(void*, size_t), size = 4 2) op new[](size_t), size = 40 5) op delete[](void* ptr)
Перегрузки operatordelete и operatordelete[] с дополнительными пользовательскими параметрами («формы размещения», (15,16)) могут объявляться в глобальной области видимости обычным образом. Они вызываются соответствующими формами размещения выражений new, если конструктор выделяемого объекта выбрасывает исключение.
Формы размещения operatordelete и operatordelete[] из стандартной библиотеки (13,14) нельзя заменить. Настроить их можно только в том случае, если в выражении размещения new не использован синтаксис ::new, предоставив соответствующую сигнатуре форму размещения delete, специфичную для класса (25,26): voidT::operatordelete(void*,void*) или voidT::operatordelete[](void*,void*).
Перегрузки, специфичные для класса
Функции освобождения памяти (17-24) могут быть определены как статические функции-члены класса. Если такие функции освобождения памяти предоставлены, они вызываются выражениями delete при удалении объектов (17,19,21) и массивов (18,20,22) этого класса, если только в выражении delete не использована форма ::delete, обходящая поиск в области видимости класса. Ключевое слово static в объявлениях этих функций необязательно: независимо от того, используется ли это ключевое слово, функция освобождения памяти всегда является статической функцией-членом.
Выражение delete начинает поиск имени подходящей функции освобождения памяти в области видимости класса (для формы массива поиск ведётся в области видимости класса элемента массива) и, если члены не найдены, продолжает его в глобальной области видимости, как обычно. Обратите внимание: согласно правилам поиска имён, любые функции освобождения памяти, объявленные в области видимости класса, скрывают все глобальные функции освобождения памяти.
Если статический тип удаляемого объекта отличается от его динамического типа (например, при удалении полиморфного объекта через указатель на базовый класс) и деструктор статического типа является виртуальным, для формы delete одиночного объекта поиск имени функции освобождения памяти начинается с места определения окончательной реализации его виртуального деструктора. Чтобы код скомпилировался, статически видимая версия operatordelete должна быть доступна независимо от того, какая функция освобождения памяти будет выполнена во время работы программы. В остальных случаях — при удалении массива через указатель на базовый класс или при удалении через указатель на базовый класс с невиртуальным деструктором — поведение не определено.
Если перегрузка с одним аргументом (17,18) не предоставлена, но предоставлена перегрузка с учётом размера, принимающая std::size_t в качестве второго параметра (21,22), при обычном освобождении памяти вызывается форма с учётом размера, а среда выполнения C++ передаёт размер освобождаемого объекта в качестве второго аргумента. Если определены обе формы, вызывается версия без учёта размера.
#include <cstddef>
#include <iostream>
// sized class-specific deallocation functions
struct X
{
static void operator delete(void* ptr, std::size_t sz)
{
std::cout << "custom delete for size " << sz << '\n';
::operator delete(ptr);
}
static void operator delete[](void* ptr, std::size_t sz)
{
std::cout << "custom delete for size " << sz << '\n';
::operator delete[](ptr);
}
};
int main()
{
X* p1 = new X;
delete p1;
X* p2 = new X[10];
delete[] p2;
}
Возможный вывод:
custom delete for size 1 custom delete for size 18
Перегрузки operator delete и operator delete[] с дополнительными пользовательскими параметрами («формы размещения», (25,26)) также могут быть определены как члены класса. Когда неудавшееся выражение размещения new ищет соответствующую функцию размещения delete для вызова, поиск начинается в области видимости класса, а затем выполняется поиск в глобальной области видимости. Ищется функция с сигнатурой, соответствующей размещению new:
#include <cstddef>
#include <iostream>
#include <stdexcept>
struct X
{
X() { throw std::runtime_error("X(): std::runtime_error"); }
// custom placement new
static void* operator new(std::size_t sz, bool b)
{
std::cout << "custom placement new called, b = " << b << '\n';
return ::operator new(sz);
}
// custom placement delete
static void operator delete(void* ptr, bool b)
{
std::cout << "custom placement delete called, b = " << b << '\n';
::operator delete(ptr);
}
};
int main()
{
try
{
[[maybe_unused]] X* p1 = new (true) X;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << '\n';
}
} Вывод:
custom placement new called, b = 1 custom placement delete called, b = 1 X(): std::runtime_error
Если оператор delete на уровне класса является шаблонной функцией, он должен иметь возвращаемый тип void, первый аргумент void* и два или более параметров. Иными словами, шаблонами могут быть только формы размещения. Экземпляр шаблона никогда не является обычной функцией освобождения памяти, независимо от его сигнатуры. Специализация шаблонного оператора delete выбирается с помощью вывода аргументов шаблона.
Примечания
Вызов T::operator delete для полиморфного класса — единственный случай, когда статическая функция-член вызывается посредством динамической диспетчеризации.
| Следующие функции должны быть потокобезопасными:
Вызовы этих функций, выделяющие или освобождающие определённую область памяти, образуют единый полный порядок; каждый такой вызов освобождения памяти происходит раньше следующего вызова выделения памяти (если он есть) в этом порядке. | (начиная с C++11) |
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_sized_deallocation | 201309L | (C++14) | Освобождение памяти с учётом размера |
__cpp_impl_destroying_delete | 201806L | (C++20) | Оператор delete с уничтожением (поддержка компилятора) |
__cpp_lib_destroying_delete | 201806L | (C++20) | Оператор delete с уничтожением (поддержка библиотеки) |
Отчёты об исправлениях
Следующие отчёты об исправлениях, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применён к | Поведение согласно опубликованной версии | Правильное поведение |
|---|---|---|---|
| CWG 220 | C++98 | пользовательским функциям освобождения памяти разрешалось выбрасывать исключения | выбрасывание исключения из функции освобождения памяти приводит к неопределённому поведению |
| CWG 1438 | C++98 | любое использование недопустимого значения указателя приводило к неопределённому поведению | неопределённое поведение возникает только при разыменовании и освобождении памяти |
| LWG 206 | C++98 | замена (2) не влияла на поведение по умолчанию (10) | поведение по умолчанию соответственно изменяется |
| LWG 298 | C++98 | замена (1) не влияла на поведение по умолчанию (9) | поведение по умолчанию соответственно изменяется |
| LWG 404 | C++98 | замены заменяемых функций освобождения памяти можно было объявлять inline | запрещено, диагностика не требуется |
| LWG 2458 | C++14 | были специфицированы перегрузки, принимающие (void*, std::size_t, const std::nothrow_t&), но вызвать их было невозможно | лишние перегрузки удалены |
См. также
|
[статическая] (C++23) | освобождает память, ранее полученную из operator new (открытая статическая функция-член std::generator<Ref,V,Allocator>::promise_type) |
| функции выделения памяти (функция) |
|
|
(устарело в C++17)(удалено в C++20) | освобождает неинициализированную память (шаблон функции) |
| освобождает ранее выделенную память (функция) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/memory/new/operator_delete