operator delete, operator delete[]
Определено в заголовочном файле <new> | ||
заменяемые обычные функции освобождения | ||
(1) | ||
void operator delete (void* ptr )throw(); | (до C++11) | |
void operator delete (void* ptr )noexcept; | (начиная с C++11) | |
(2) | ||
void operator delete[](void* ptr )throw(); | (до 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) |
заменяемые размещающие функции освобождения | ||
(9) | ||
void operator delete (void* ptr, conststd::nothrow_t& tag )throw(); | (до C++11) | |
void operator delete (void* ptr, conststd::nothrow_t& tag )noexcept; | (начиная с C++11) | |
(10) | ||
void operator delete[](void* ptr, conststd::nothrow_t& tag )throw(); | (до C++11) | |
void operator delete[](void* ptr, conststd::nothrow_t& tag )noexcept; | (начиная с C++11) | |
void operator delete (void* ptr, std::align_val_t al, conststd::nothrow_t& tag )noexcept; | (11) | (начиная с C++17) |
void operator delete[](void* ptr, std::align_val_t al, conststd::nothrow_t& tag )noexcept; | (12) | (начиная с C++17) |
нераспределяющие размещающие функции освобождения | ||
(13) | ||
void operator delete (void* ptr, void* place )throw(); | (до C++11) | |
void operator delete (void* ptr, void* place )noexcept; | (начиная с C++11) | |
(14) | ||
void operator delete[](void* ptr, void* place )throw(); | (до C++11) | |
void operator delete[](void* ptr, void* place )noexcept; | (начиная с C++11) | |
определяемые пользователем размещающие функции освобождения | ||
void operator delete (void* ptr, аргументы... ); | (15) | |
void operator delete[](void* ptr, аргументы... ); | (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, аргументы... ); | (25) | |
void T::operator delete[](void* ptr, аргументы... ); | (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. Эти функции освобождения вызываются выражениями delete и выражениями new для освобождения памяти после уничтожения (или невозможности создания) объектов с динамической длительностью хранения. Их также можно вызывать с использованием обычного синтаксиса вызова функций.
T
.T
.T
.T
.operator delete
. Вместо этого прямой вызов деструктора, например p->~T();, становится обязанностью этого определяемого пользователем оператора удаления.Смотрите выражение delete для точной информации о правилах разрешения перегрузок между перегрузками с учётом выравнивания и без учёта выравнивания обычных (неразмещающих) функций освобождения. | (начиная с C++17) |
Во всех случаях, если ptr является нулевым указателем, стандартные функции освобождения библиотеки ничего не делают. Если указатель, переданный функции освобождения стандартной библиотеки, не был получен из соответствующей функции выделения стандартной библиотеки, поведение не определено.
После возврата из стандартной библиотечной функции освобождения все указатели, ссылающиеся на любую часть освобождённого хранилища, становятся недействительными.
Косвенное обращение через указатель, который таким образом стал недействительным, и передача его функции освобождения (двойное удаление) это неопределённое поведение. Любое другое использование определяется реализацией.
Содержание |
[править]Параметры
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) неявно объявлены в каждой единице трансляции, даже если заголовок <new> не включен. Эти функции являются заменяемыми: предоставляемая пользователем функция, не являющаяся элементом, с той же сигнатурой, определённой в любом месте программы, в любом исходном файле, заменяет соответствующую неявную версию для всей программы. Её объявление не обязательно должно быть видимым.
Программа некорректна, диагностика не требуется, если в программе предусмотрено более одной замены или если замена объявлена с помощью спецификатора inline
. Программа некорректна, если замена определена в пространстве имён, отличном от глобального пространства имён, или если она определена как статическая функция, не являющаяся элементом, в глобальной области видимости.
Стандартные библиотечные реализации версий (9,10) не генерирующих исключения напрямую вызывают соответствующие версии (1,2) генерирующие исключения. Стандартные библиотечные реализации функций освобождения с учётом размера (5-8) напрямую вызывают соответствующие функции освобождения без учёта размера (1-4). Стандартные библиотечные реализации форм массива без учёта размера (2,4) напрямую вызывают соответствующие однообъектные формы (1,3). Таким образом, замены функций освобождения отдельных объектов (1,3) генерирующих исключения достаточно для обработки всех освобождений.
Глобальная замена operator
new/delete:
#include <cstdio>#include <cstdlib>#include <new> // не встраиваемая, требуется [replacement.functions]/3void*operator new(std::size_t sz){std::printf("1) new(size_t), размер = %zu\n", sz);if(sz ==0)++sz;// исправляет std::malloc(0), который может вернуть nullptr в случае успеха if(void*ptr =std::malloc(sz))return ptr; throwstd::bad_alloc{};// требуется [new.delete.single]/3} // не встраиваемая, требуется [replacement.functions]/3void*operator new[](std::size_t sz){std::printf("2) new[](size_t), размер = %zu\n", sz);if(sz ==0)++sz;// исправляет std::malloc(0), который может вернуть nullptr в случае успеха if(void*ptr =std::malloc(sz))return ptr; throwstd::bad_alloc{};// требуется [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), размер = %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), размер = %zu\n", size);std::free(ptr);} int main(){int* p1 = new int; delete p1; int* p2 = new int[10];// гарантированно вызовет замену в C++11 delete[] p2;}
Возможный вывод:
// Скомпилировано с помощью GCC-5 в режиме C++17, чтобы получить следующее: 1) op new(size_t), размер = 4 4) op delete(void*, size_t), размер = 4 2) op new[](size_t), размер = 40 5) op delete[](void* ptr)
Перегрузки operator delete
и operator delete[]
с дополнительными пользовательскими параметрами ("размещающие формы", (15,16)) могут быть объявлены в глобальной области видимости как обычно, и вызваны соответствующими размещающими формами выражений new, если конструктор объекта, для которого выделяется память, генерирует исключение.
Стандартные библиотечные размещающие формы operator delete
(13,14) не могут быть заменены и могут быть настроены только в том случае, если размещающее выражение new не использовало синтаксис ::new, предоставляя специфичный для класса размещающий delete (25,26) с соответствующей сигнатурой: void T::operator delete(void*, void*) или void T::operator delete[](void*, void*).
[править]Специфичные для класса перегрузки
Функции освобождения (17-24) могут быть определены как статические функции-элементы класса. Эти функции освобождения, если они предусмотрены, вызываются выражениями delete при удалении объектов (17,19,21) и массивов (18,20,22) этого класса, если только выражение delete использовало форму ::delete, которая обходит поиск в области видимости класса. Ключевое слово static является необязательным для этих объявлений функций: независимо от того, используется ключевое слово или нет, функция освобождения всегда является статической функцией-элементом.
Выражение delete ищет соответствующее имя функции освобождения, начиная с области видимости класса (форма массива ищет в области видимости класса элементов массива) и переходит к глобальной области видимости, если элементы не найдены, как обычно. Обратите внимание, что в соответствии с правилами поиска имён, любые функции освобождения, объявленные в области видимости класса, скрывают все глобальные функции освобождения.
Если статический тип удаляемого объекта отличается от его динамического типа (например, при удалении полиморфного объекта с помощью указателя на базовый класс) и если деструктор в статическом типе является виртуальным, начинается поиск в форме delete для одного объекта имени функции освобождения, начиная с точки определения последнего переопределения её виртуального деструктора. Независимо от того, какая функция освобождения будет выполняться во время выполнения, статически видимая версия оператора delete должна быть доступна для компиляции. В других случаях при удалении массива через указатель на базовый класс или при удалении через указатель на базовый класс с не виртуальным деструктором поведение не определено.
Если перегрузка с одним аргументом (17,18) не указана, но перегрузка с учётом размера принимает std::size_t в качестве второго параметра (21,22), форма с учётом размера вызывается для обычного освобождения, а среда выполнения C++ передаёт размер освобождаемого объекта в качестве второго аргумента. Если определены обе формы, вызывается версия без учёта размера.
#include <iostream> // специфические для класса функции освобождения памятиstruct X {staticvoid operator delete(void* ptr, std::size_t sz){std::cout<<"пользовательский delete для размера "<< sz <<'\n';::operator delete(ptr);} staticvoid operator delete[](void* ptr, std::size_t sz){std::cout<<"пользовательский delete для размера "<< sz <<'\n';::operator delete[](ptr);}}; int main(){ X* p1 = new X; delete p1; X* p2 = new X[10]; delete[] p2;}
Возможный вывод:
пользовательский delete для размера 1 пользовательский delete для размера 18
Перегрузки operator delete
и operator delete[]
с дополнительными пользовательскими параметрами ("размещающие формы", (25,26)) также могут быть определены как элементы класса. Когда выражение неудачного размещающего new ищет соответствующую размещающую функцию delete для вызова, оно начинает поиск в области видимости класса, прежде чем проверять глобальную область видимости, и ищет функцию с сигнатурой, соответствующего размещающего new:
#include <iostream>#include <stdexcept> struct X { X(){throwstd::runtime_error("X(): std::runtime_error");} // пользовательский размещающий newstaticvoid*operator new(std::size_t sz, bool b){std::cout<<"пользовательский размещающий new вызван, b = "<< b <<'\n';return::operator new(sz);} // пользовательский размещающий deletestaticvoid operator delete(void* ptr, bool b){std::cout<<"пользовательский размещающий delete вызван, b = "<< b <<'\n';::operator delete(ptr);}}; int main(){try{[[maybe_unused]] X* p1 = new (true) X;}catch(conststd::exception& ex){std::cout<< ex.what()<<'\n';}}
Вывод:
пользовательский размещающий new вызван, b = 1 пользовательский размещающий delete вызван, b = 1 X(): std::runtime_error
Если operator delete
уровня класса является шаблонной функцией, она должна иметь возвращаемый тип void, первый аргумент void* и должна иметь два или более параметра. Другими словами, шаблонами могут быть только размещающие формы. Экземпляр шаблона никогда не является обычной функцией освобождения, независимо от его сигнатуры. Специализация оператора delete шаблона выбирается с выводом аргумента шаблона.
[править]Примечание
Вызов специфичного для класса T::operator delete в полиморфном классе это единственный случай, когда статическая функция-элемент вызывается посредством динамической диспетчеризации.
Если поведение функции освобождения не соответствует ограничениям по умолчанию, поведение не определено.
Следующие функции должны быть потокобезопасными:
Вызовы этих функций, которые выделяют или освобождают конкретную единицу памяти, происходят в одном общем порядке, и каждый такой вызов освобождения происходит до следующего выделения памяти (если таковое имеется) в этом порядке. | (начиная с C++11) |
Макрос тест функциональности | Значение | Стандарт | Комментарий |
---|---|---|---|
__cpp_sized_deallocation | 201309L | (C++14) | Освобождение по размеру |
__cpp_impl_destroying_delete | 201806L | (C++20) | Удаляющий operator delete (поддержка компилятора) |
__cpp_lib_destroying_delete | 201806L | (C++20) | Удаляющий operator delete (поддержка библиотеки) |
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
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++17)(удалено в C++20) | освобождает неинициализированное хранилище (шаблон функции) |
освобождает ранее выделенную память (функция) |