Spec-Zone.ru › C++

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 для освобождения памяти после уничтожения объектов (или неудачной попытки их создания) с динамической продолжительностью хранения. Их также можно вызывать с помощью обычного синтаксиса вызова функции.

1-12) Заменяемые функции освобождения памяти. Стандартная библиотека предоставляет реализации этих функций по умолчанию; сведения об их действии см. ниже.
1-8) Вызываются выражениями delete и delete[]. Делают недействительным любой ненулевой ptr.
9-12) Вызываются выражениями размещения new при ошибке инициализации. operatordelete[] делает недействительным любой ненулевой 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[].
13,14) Вызываются выражениями размещения new, использовавшими функцию выделения памяти для размещения без выделения памяти, если какая-либо часть инициализации в выражении завершается выбрасыванием исключения. Не выполняют никаких действий.
15-30) Пользовательские функции освобождения памяти, вызываемые выражениями delete, delete[] и размещения new.
27-30) Если определены, выражения delete не вызывают деструктор *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) являются заменяемыми. Реализации по умолчанию действуют следующим образом:

1) Если ptr является нулевым, ничего не делает. В противном случае освобождает память, выделенную более ранним вызовом operatornew.
2) Вызывает operator delete(ptr) так, как если бы перегрузка (1) могла освободить память, выделенную более ранним вызовом operatornew[].
3) То же, что и (1).
4) Вызывает operator delete(ptr, al) так, как если бы перегрузка (3) могла освободить память, выделенную более ранним вызовом operatornew[].
5) Вызывает operator delete(ptr).
6) Вызывает operator delete[](ptr).
7) Вызывает operator delete(ptr, al).
8) Вызывает operator delete[](ptr, al).
9) Вызывает operator delete(ptr).
10) Вызывает operator delete[](ptr).
11) Вызывает operator delete(ptr, al).
12) Вызывает 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 для полиморфного класса — единственный случай, когда статическая функция-член вызывается посредством динамической диспетчеризации.

Следующие функции должны быть потокобезопасными:

  • Версии функций из библиотеки operator new и operator delete
  • Пользовательские замены глобальных operator new и operator delete
  • std::calloc, std::malloc, std::realloc, std::aligned_alloc(начиная с C++17), std::free

Вызовы этих функций, выделяющие или освобождающие определённую область памяти, образуют единый полный порядок; каждый такой вызов освобождения памяти происходит раньше следующего вызова выделения памяти (если он есть) в этом порядке.

(начиная с 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&), но вызвать их было невозможно
лишние перегрузки удалены

См. также

operator delete
[статическая] (C++23)
освобождает память, ранее полученную из operator new
(открытая статическая функция-член std::generator<Ref,V,Allocator>::promise_type)
operator newoperator new[]
функции выделения памяти
(функция)
return_temporary_buffer
(устарело в C++17)(удалено в C++20)
освобождает неинициализированную память
(шаблон функции)
free
освобождает ранее выделенную память
(функция)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/memory/new/operator_delete

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API