Spec-Zone.ru › C++

operator new, operator new[]

Объявлено в заголовке <new>
Заменяемые функции выделения памяти
void* operator new  ( std::size_t count );
(1)
void* operator new[]( std::size_t count );
(2)
void* operator new  ( std::size_t count, std::align_val_t al );
(3) (начиная с C++17)
void* operator new[]( std::size_t count, std::align_val_t al );
(4) (начиная с C++17)
Заменяемые небросающие исключения функции выделения памяти
void* operator new  ( std::size_t count, const std::nothrow_t& tag );
(5) (noexcept начиная с C++11)
void* operator new[]( std::size_t count, const std::nothrow_t& tag );
(6) (noexcept начиная с C++11)
void* operator new  ( std::size_t count, std::align_val_t al,
                      const std::nothrow_t& tag ) noexcept;
(7) (начиная с C++17)
void* operator new[]( std::size_t count, std::align_val_t al,
                      const std::nothrow_t& tag ) noexcept;
(8) (начиная с C++17)
Невыделяющие функции размещения
void* operator new  ( std::size_t count, void* ptr );
(9) (noexcept начиная с C++11)
(constexpr начиная с C++26)
void* operator new[]( std::size_t count, void* ptr );
(10) (noexcept начиная с C++11)
(constexpr начиная с C++26)
Пользовательские функции размещения
void* operator new  ( std::size_t count, /* args... */ );
(11)
void* operator new[]( std::size_t count, /* args... */ );
(12)
void* operator new  ( std::size_t count,
                      std::align_val_t al, /* args... */ );
(13) (начиная с C++17)
void* operator new[]( std::size_t count,
                      std::align_val_t al, /* args... */ );
(14) (начиная с C++17)
Специфичные для класса функции выделения памяти
void* T::operator new  ( std::size_t count );
(15)
void* T::operator new[]( std::size_t count );
(16)
void* T::operator new  ( std::size_t count, std::align_val_t al );
(17) (начиная с C++17)
void* T::operator new[]( std::size_t count, std::align_val_t al );
(18) (начиная с C++17)
Специфичные для класса функции размещения
void* T::operator new  ( std::size_t count, /* args... */ );
(19)
void* T::operator new[]( std::size_t count, /* args... */ );
(20)
void* T::operator new  ( std::size_t count,
                         std::align_val_t al, /* args... */ );
(21) (начиная с C++17)
void* T::operator new[]( std::size_t count,
                         std::align_val_t al, /* args... */ );
(22) (начиная с C++17)

Пытается выделить запрошенное количество байтов; запрос на выделение памяти может завершиться неудачей (даже если запрошенное количество байтов равно нулю). Эти функции выделения памяти вызываются new-выражениями для выделения памяти, в которой затем будет инициализирован новый объект. Их также можно вызывать с помощью обычного синтаксиса вызова функции.

1-8) Заменяемые функции выделения памяти. Стандартная библиотека предоставляет реализации этих функций по умолчанию; описание их эффектов см. ниже.
9,10) Вызываются стандартными размещающими new-выражениями. Не выполняют никаких действий и возвращают ptr без изменений.
Если эта функция вызывается через размещающее new-выражение, а ptr является нулевым указателем, поведение не определено.
11-22) Пользовательские функции выделения памяти, вызываемые new-выражениями.

Перегрузки (1-4) неявно объявляются в каждой единице трансляции, даже если заголовок <new> не включен.

Критерии выбора перегрузки см. в описании new-выражения.

Параметры

count - количество байтов для выделения
ptr - указатель на область памяти, в которой нужно инициализировать объект
tag - тег для устранения неоднозначности, используемый для выбора небросающих исключения перегрузок
al - используемое выравнивание; недопустимое значение приводит к неопределенному поведению

Возвращаемое значение

1-4) Если выделение памяти прошло успешно, возвращает ненулевой указатель p0, указывающий на выровненную соответствующим образом область памяти размером не менее size и отличный от любого ранее возвращенного значения p1, если только это значение p1 впоследствии не было передано заменяемой функции освобождения памяти; если выделение памяти завершилось неудачей, не возвращает управление (выбрасывается исключение; см. ниже).
5-8) То же, что и (1-4), но при неудачном выделении памяти возвращает нулевой указатель.
9,10) ptr
11-22) То же, что и (1-4), если функция не возвращает управление при неудачном выделении памяти; в противном случае — то же, что и (5-8).

Исключения

1-4) При невозможности выделить память выбрасывает исключение типа, который соответствовал бы обработчику типа std::bad_alloc.
11-22) То же, что и (1-4), если функция не возвращает управление при неудачном выделении памяти; в противном случае — то же, что и (5-8).

Глобальные замены

Перегрузки (1-8) являются заменяемыми. Эффекты версий по умолчанию:

1) Пытается выделить запрошенную область памяти. Не определено, включает ли эта попытка вызов std::malloc или std::aligned_alloc.
  • Если попытка успешна, возвращает указатель на выделенную область памяти; память выровнена по фундаментальному выравниванию(до C++17)память выровнена по __STDCPP_DEFAULT_NEW_ALIGNMENT__(начиная с C++17).
  • В противном случае, если в данный момент не установлен обработчик new, выбрасывает std::bad_alloc.
  • В противном случае вызывает установленный в данный момент обработчик new.
    • Если обработчик new возвращает управление, начинает новую попытку выделения памяти.
    • В противном случае завершает текущий вызов.
2) Возвращает operator new(count).
3) То же, что и (1), за исключением того, что возвращаемая область памяти выровнена по al.
4) Возвращает operator new(count, al).
5-8) Вызывает соответственно (1-4) с теми же аргументами, за исключением tag.
  • Если вызов завершается нормально, возвращает результат этого вызова.
  • В противном случае возвращает нулевой указатель.

В автономных реализациях поведение версий по умолчанию (1-8) на соответствие указанным выше требованиям определяется реализацией. Для автономных реализаций рекомендуется, чтобы, если какая-либо из версий по умолчанию соответствует требованиям реализации с размещенной средой, им соответствовали все такие версии.

(начиная с C++26)

Замена глобальных функций 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)

Перегрузки operator new и operator new[] с дополнительными пользовательскими параметрами («формы размещения») могут, как обычно, объявляться в глобальной области видимости и вызываются соответствующими формами размещения new-выражений.

Невыделяющие формы размещения operator new стандартной библиотеки (9,10) нельзя заменять; их можно настроить, только если в размещающем new-выражении не использован синтаксис ::new, определив соответствующую по сигнатуре специфичную для класса функцию размещения new (19,20): void*T::operatornew(std::size_t,void*) или void*T::operatornew[](std::size_t,void*).

Форма размещения void*operatornew(std::size_t,std::size_t) не допускается, поскольку соответствующая сигнатура функции освобождения памяти, voidoperatordelete(void*,std::size_t), относится к обычной (не размещающей) функции освобождения памяти.

(начиная с C++14)

Перегрузки, специфичные для класса

Функции выделения памяти как для одиночных объектов, так и для массивов могут быть определены в классе как открытые статические функции-члены (версии (15-18)). Если они определены, эти функции выделения памяти вызываются new-выражениями для выделения памяти под одиночные объекты и массивы этого класса, если только в new-выражении не использована форма ::new, обходящая поиск в области видимости класса. Ключевое слово static для этих функций необязательно: независимо от того, используется оно или нет, функция выделения памяти является статической функцией-членом.

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

При выделении памяти под объекты и массивы объектов, выравнивание которых превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__, разрешение перегрузки выполняется дважды: сначала для сигнатур функций, учитывающих выравнивание, затем для сигнатур, не учитывающих выравнивание. Это означает, что если у класса с расширенным выравниванием new есть специфичная для класса функция выделения памяти, не учитывающая выравнивание, будет вызвана именно она, а не глобальная функция выделения памяти, учитывающая выравнивание. Это сделано намеренно: предполагается, что функция-член класса лучше всего знает, как обрабатывать этот класс.

(начиная с C++17)

При выделении памяти под объекты и массивы объектов, выравнивание которых не превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__, разрешение перегрузки выполняется дважды: сначала для сигнатур функций, не учитывающих выравнивание, затем для сигнатур, учитывающих выравнивание.

(начиная с C++20)
#include <cstddef>
#include <iostream>

// class-specific allocation functions
struct X
{
    static void* operator new(std::size_t count)
    {
        std::cout << "custom new for size " << count << '\n';
        return ::operator new(count);
    }
    
    static void* operator new[](std::size_t count)
    {
        std::cout << "custom new[] for size " << count << '\n';
        return ::operator new[](count);
    }
};

int main()
{
    X* p1 = new X;
    delete p1;
    X* p2 = new X[10];
    delete[] p2;
}

Возможный вывод:

custom new for size 1
custom new[] for size 10

Перегрузки operator new и operator new[] с дополнительными пользовательскими параметрами («формы размещения») также могут определяться как функции-члены класса (19-22). Когда размещающее new-выражение с соответствующей сигнатурой ищет вызываемую функцию выделения памяти, поиск начинается в области видимости класса, а затем продолжается в глобальной области видимости; если предоставлена специфичная для класса функция размещения new, вызывается она.

При выделении памяти под объекты и массивы объектов, выравнивание которых превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__, разрешение перегрузки для форм размещения выполняется дважды, как и для обычных форм: сначала для сигнатур функций, учитывающих выравнивание, затем для сигнатур, не учитывающих выравнивание.

(начиная с C++17)

При выделении памяти под объекты и массивы объектов, выравнивание которых не превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__, разрешение перегрузки для форм размещения выполняется дважды, как и для обычных форм: сначала для сигнатур функций, не учитывающих выравнивание, затем для сигнатур, учитывающих выравнивание.

(начиная с C++20)
#include <cstddef>
#include <iostream>
#include <stdexcept>

struct X
{
    X() { throw std::runtime_error(""); }
    
    // custom placement new
    static void* operator new(std::size_t count, bool b)
    {
        std::cout << "custom placement new called, b = " << b << '\n';
        return ::operator new(count);
    }
    
    // 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&)
    {}
}

Вывод:

custom placement new called, b = 1
custom placement delete called, b = 1

Если operator new на уровне класса является шаблонной функцией, она должна иметь возвращаемый тип void*, первый аргумент std::size_t и два или более параметра. Иными словами, шаблонами могут быть только формы размещения.

Примечания

Хотя невыделяющий размещающий new (9,10) нельзя заменить, в области видимости класса, как описано выше, можно определить функцию с такой же сигнатурой. Кроме того, допускаются глобальные перегрузки, похожие на размещающий new, но принимающие указатель не типа void в качестве второго аргумента. Поэтому код, который должен гарантировать вызов настоящего размещающего new (например, std::allocator::construct), должен использовать ::new, а также приводить указатель к типу void*.

Если поведение функции освобождения памяти не удовлетворяет ограничениям по умолчанию, поведение не определено.

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

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

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

(начиная с C++11)

Не определено, выполняют ли версии operator new из библиотеки какие-либо вызовы std::malloc или std::aligned_alloc(начиная с C++17).

Для загрузки большого файла предпочтительнее использовать отображение файла в память с помощью функций, специфичных для операционной системы, например mmap в POSIX или CreateFileMapping(A/W) вместе с MapViewOfFile в Windows, а не выделять буфер для чтения файла.

Макрос проверки поддержки функции Значение Стандарт Функция
__cpp_lib_freestanding_operator_new 202306L (C++26) поддержка заменяемого operatornew в автономных реализациях[1]
0 (C++26) поддержка в автономных реализациях отсутствует
__cpp_lib_constexpr_new 202406L (C++26) размещающие new и new[] с constexpr
  1. Формально этот макрос раскрывается в 202306L, если все версии по умолчанию заменяемых глобальных функций выделения памяти соответствуют требованиям реализации с размещенной средой.

Отчеты об ошибках

Следующие отчеты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.

DR Применяется к Поведение согласно опубликованной версии Правильное поведение
CWG 521 C++98 можно было выбросить любой класс, производный от std::bad_alloc,
даже если базовый класс std::bad_alloc неоднозначен или недоступен
выброшенное исключение должно соответствовать
обработчику типа std::bad_alloc
LWG 9 C++98 несколько вызовов для выделения нуля
байтов могли возвращать один и тот же указатель
это допускается, только если все ранее
возвращенные таким образом указатели
были переданы функциям освобождения памяти
LWG 206 C++98 замена заменяемых функций выделения памяти
не влияла на поведение по умолчанию соответствующих
заменяемых небросающих исключения функций выделения памяти
поведение по умолчанию
соответствующим образом меняется
LWG 404 C++98 замены заменяемых функций выделения памяти
можно было объявлять с ключевым словом inline
запрещено; диагностика не требуется

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
    • 17.7 Управление динамической памятью [support.dynamic]
  • Стандарт C++20 (ISO/IEC 14882:2020):
    • 17.6 Управление динамической памятью [support.dynamic]
  • Стандарт C++17 (ISO/IEC 14882:2017):
    • 21.6 Управление динамической памятью [support.dynamic]
  • Стандарт C++14 (ISO/IEC 14882:2014):
    • 18.6 Управление динамической памятью [support.dynamic]
  • Стандарт C++11 (ISO/IEC 14882:2011):
    • 18.6 Управление динамической памятью [support.dynamic]
  • Стандарт C++03 (ISO/IEC 14882:2003):
    • 18.4 Управление динамической памятью [lib.support.dynamic]
  • Стандарт C++98 (ISO/IEC 14882:1998):
    • 18.4 Управление динамической памятью [lib.support.dynamic]

См. также

operator new
[static] (C++23)
выделяет память с помощью Allocator
(открытая статическая функция-член std::generator<Ref,V,Allocator>::promise_type)
operator deleteoperator delete[]
функции освобождения памяти
(функция)
get_new_handler
(C++11)
получает текущий обработчик new
(функция)
set_new_handler
устанавливает новый обработчик
(функция)
get_temporary_buffer
(устарело в C++17)(удалено в C++20)
получает неинициализированную область памяти
(шаблон функции)
malloc
выделяет память
(функция)
aligned_alloc
(C++17)
выделяет выровненную память
(функция)

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

Spec-Zone.ru

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