Что возвращает оператор new c
Перейти к содержимому

Что возвращает оператор new c

  • автор:

Работа с памятью с помощью new и delete

Как известно, в языке С для динамического выделения и освобождения памяти используются фун­кции malloc() и free(). Вместе с тем С++ содержит два оператора, выполняющих выделение и освобождение памяти более эффективно и более просто. Этими операторами являются new и delete. Их общая форма имеет вид:

переменная_указатель = new тип_переменной;

Здесь переменная_указaтель является указателем типа тип_переменной. Оператор new выделяет память для хранения значения типа тип_переменной и возвращает ее адрес. С помощью new могут быть размещены любые типы данных. Оператор delete освобождает память, на которую указывает указатель переменная_указатель.

Если операция выделения памяти не может быть выполнена, то оператор new генерирует ис­ключение типа xalloc. Если программа не перехватит это исключение, тогда она будет снята с выполнения. Хотя для коротких программ такое поведение по умолчанию является удовлетвори­тельным, для реальных прикладных программ обычно требуется перехватить исключение и обра­ботать его соответствующим образом. Для того чтобы отследить это исключение, необходимо вклю­чить заголовочный файл except.h.

Оператор delete следует использовать только для указателей на память, выделенную с исполь­зованием оператора new. Использование оператора delete с другими типами адресов может по­родить серьезные проблемы.

Есть ряд преимуществ использования new перед использованием malloc(). Во-первых, оператор new автоматически вычисляет размер необходимой памяти. Нет необходимости в использовании оператора sizeof(). Более важно то, что он предотвращает случайное выделение неправильного количества памяти. Во-вторых, оператор new автоматически возвращает указатель требуемого типа, так что нет необходимости в использовании оператора преобразования типа. В-третьих, как ско­ро будет описано, имеется возможность инициализации объекта при использовании оператора new. И наконец, имеется возможность перегрузить оператор new и оператор delete глобально или по отношению к тому классу, который создается.

Ниже приведен простой пример использования операторов new и delete. Следует обратить вни­мание на использование блока try/catch для отслеживания ошибок выделения памяти.

#include
#include
int main()
int *p;
try p = new int; // выделение памяти для int
> catch (xalloc xa) cout return 1;
>
*p = 20; // присвоение данному участку памяти значения 20
cout delete р; // освобождение памяти
return 0;
>

Эта программа присваивает переменной р адрес блока памяти, имеющего достаточный размер для того, чтобы содержать число целого типа. Далее этой памяти присваивается значение и содер­жимое памяти выводится на экран. Наконец, динамически выделенная память освобождается.

Как отмечалось, можно инициализировать память с использованием оператора new. Для этого надо указать инициализирующее значение в скобках после имени типа. Например, в следующем примере память, на которую указывает указатель р, инициализируется значением 99:

#include
#include
int main()
int *p;
try p = new int (99); // инициализация 99-ю
> catch (xalloc xa) cout return 1;
>
cout delete p;
return 0;
>

С помощью new можно размещать массивы. Общая форма для одномерного массива имеет вид:

переменная_указатель = new тип_переменной [размер];

Здесь размер определяет число элементов в массиве. Необходимо запомнить важное ограничение при размещении массива: его нельзя инициализировать.

Для освобождения динамически размещенного массива необходимо использовать следующую форму оператора delete:

Здесь скобки [] информируют оператор delete, что необходимо освободить память, выделенную для массива.

В следующей программе выделяется память для массива из 10 элементов типа float. Элементам массива присваиваются значения от 100 до 109, а затем содержимое массива выводится на экран:

#include
#include
int main()
float *p;
int i;
try p = new float [10]; // получение десятого элемента массива
> catch(xalloc xa) cout return 1;
>
// присвоение значений от 100 до 109
for (i=0; i // вывод содержимого массива
for (i=0; i delete [] p; // удаление всего массива
return 0;
>

  • Размещение объектов
  • Еще один способ контроля за выделением памяти
  • Перегрузка new u delete

[C++] Всё ли мы знаем об операторах new и delete?

Привет! Ниже речь пойдет об известных всем операторах new и delete, точнее о том, о чем не пишут в книгах (по крайней мере в книгах для начинающих).
На написание данной статьи меня побудило часто встречаемое заблуждение по поводу new и delete, которое я постоянно вижу на форумах и даже(. ) в некоторых книгах.
Все ли мы знаем, что такое на самом деле new и delete? Или только думаем, что знаем?
Эта статья поможет вам разобраться с этим (ну, а те, кто знают, могут покритиковать:))

Note: ниже пойдет речь исключительно об операторе new, для других форм оператора new и для всех форм оператора delete все ниженаписанное также является правдой и применимо по аналогии.

Итак, начнем с того, что обычно пишут в книгах для начинающих, когда описывают new (текст взят «с потолка», но вцелом соответствует правде):

Оператор new выделяет память больше или равную требуемому размеру и, в отличие от функций языка С, вызывает конструктор(ы) для объекта(ов), под которые память выделена… вы можете перегрузить (где-то пишут реализовать) оператор new под свои нужды.

И для примера показывают примитивную перегрузку (реализацию) оператора new, прототип которого выглядит так
void* operator new (std::size_t size) throw (std::bad_alloc);

На что хочется обратить внимание:
1. Нигде не разделяют new key-word языка С++ и оператор new, везде о них говорят как об одной сущности.
2. Везде пишут, что new вызывает конструктор(ы) для объекта(ов).
И первое и второе является распространенным заблуждением.

Но не будем надеяться на книги для начинающих, обратимся к Стандарту, а именно к разделу 5.3.4 и к 18.6.1, в которых собственно и раскрывается (точнее приоткрывается) тема данной статьи.

5.3.4
The new-expression attempts to create an object of the type-id (8.1) or new-type-id to which it is applied. /*дальше нам не интересно*/
18.6.1
void* operator new(std::size_t size) throw(std::bad_alloc);
Effects: The allocation function called by a new-expression (5.3.4) to allocate size bytes of
storage suitably aligned to represent any object of that size /*дальше нам не интересно*/

Тут мы уже видим, что в первом случае new именуется как expression, а во втором он объявлен как operator. И это действительно 2 разные сущности!
Попробуем разобраться почему так, для этого нам понадобятся ассемблерные листинги, полученные после компиляции кода, использующего new. Ну, а теперь обо все по порядку.

new-expression — это оператор языка, такой же как if, while и т.д. (хотя if, while и т.д. все же именуются как statement, но отбросим лирику) Т.е. встречая его в листинге компилятор генерирует определенный код, соответствующий этому оператору. Так же new — это одно из key-words языка С++, что еще раз подтверждает его общность с if‘ами, for’ами и т.п. А operator new() в свою очередь — это просто одноименная функция языка С++, поведение которой можно переопределить. ВАЖНОoperator new() НЕ вызывает конструктор(ы) для объекта(ов), под который(ые) выделяется память. Он просто выделяет память нужного размера и все. Его отличие от сишных функций в том, что он может бросить исключение и его можно переопределить, а так же сделать оператором для отдельно взятого класса, тем самым переопределить его только для этого класса (остальное вспомните сами:)).
А вот new-expression как раз и вызывает конструктор(ы) объекта(ов). Хотя правильней сказать, что он тоже ничего не вызывает, просто, встречая его, компилятор генерирует код вызова конструктора(ов).

Для полноты картины рассмотрим следующий пример:

#include class Foo < public: Foo() < std::cout >; int main ()

после исполнения данного кода, как и ожидалось, будет напечатано «Foo()». Разберемся почему, для этого понадобится заглянуть в ассемблер, который я немного прокомментировал для удобства.
(код получен компилятором cl, используемым в MSVS 2012, хотя в основном я использую gcc, но это к делу не относится)

/Foo *bar = new Foo; push 1 ; размер в байтах для объекта Foo call operator new (02013D4h) ; вызываем operator new pop ecx mov dword ptr [ebp-0E0h],eax ; записываем указатель, вернувшийся из new, в bar and dword ptr [ebp-4],0 cmp dword ptr [ebp-0E0h],0 ; проверяем не 0 ли записался в bar je main+69h (0204990h) ; если 0, то уходим отсюда (возможно вообще из main или в какой-то обработчик, в данном случае неважно) mov ecx,dword ptr [ebp-0E0h] ; кладем указатель на выделенную память в ecx (MSVS всегда передает this в ecx(rcx)) call Foo::Foo (02011DBh) ; и вызываем конструктор ; дальше не интересно 

Для тех, кто ничего не понял, вот (почти) аналог того, что получилось на сиподобном псевдокоде (т.е. не надо пробовать это компилировать :))

Foo *bar = operator new (1); // где 1 - требуемый размер bar->Foo(); // вызываем конструктор 

Приведенный код подтверждает все, написанное выше, а именно:
1. оператор (языка) new и operator new() — это НЕ одно и тоже.
2. operator new() НЕ вызывает конструктор(ы)
3. вызов конструктора(ов) генерирует компилятор, встречая в коде key-word «new»

Итог: надеюсь, эта статья помогла вам понять разницу между new-expressionи operator new() или даже узнать, что она (эта разница) вообще существует, если кто-то не знал.

P.S. оператор delete и operator delete() имеют аналогичное различие, поэтому в начале статьи я сказал, что не буду его описывать. Думаю, теперь вы поняли, почему его описание не имеет смысла и сможете самостоятельно проверить справедливость написанного выше для delete.

Update:
Хабражитель с ником khim в личной переписке предложил следующий код, который хорошо демонстрирует суть написанного выше.

#include class Test < public: Test() < std::cout void* operator new (std::size_t size) throw (std::bad_alloc) < std::cout >; int main() < Test *t = new Test(); void *p = Test::operator new(100); // 100 для различия в выводе >

Этот код выведет следующее

Test::operator new(1) Test::Test() Test::operator new(100) 

оператор новый, оператор new[]

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

1) Вызывается немассивным new-expressions для выделения памяти, необходимой для одного объекта. Стандартная реализация library выделяет байты count из свободного хранилища. В случае сбоя стандартная реализация library вызывает указатель функции, возвращенный std::get_new_handler , и повторяет попытки выделения до тех пор, пока новый обработчик не вернет значение или не станет указателем null, после чего выдается std::bad_alloc . Эта функция требуется для возврата указателя, соответствующим образом выровненного для указания на объект запрошенного размера.

2) Вызывается массивом form из new[]-expressions для выделения всей памяти, необходимой для массива (включая возможные new-expression overhead). Стандартная реализация library вызывает версию (1)..

3) Вызывается не-массивом new-expressions для выделения памяти, необходимой для одного объекта, требования к выравниванию которого превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ .

4) Вызывается формой массива new[]-expressions для выделения всей памяти, необходимой для массива объектов, требования к выравниванию которых превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ .

5) Вызывается не бросающим не массивом new-expressions . Стандартная реализация library вызывает версию (1) и возвращает указатель null в случае сбоя вместо распространения исключения.

6) Вызывается невыбрасывающим массивом form из new[]-expressions . Стандартная реализация library вызывает версию (2) и возвращает указатель null в случае сбоя вместо распространения исключения.

7) Вызывается new-expressions , не создающим генерацию массива, когда требования к выравниванию объекта превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Стандартная реализация library вызывает версию (3) и возвращает указатель null в случае сбоя вместо распространения исключения.

8) Вызывается негенерирующим массивом form из new[]-expressions , когда требования к выравниванию элементов массива превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Стандартная реализация library вызывает версию (4) и возвращает указатель null в случае сбоя вместо распространения исключения.

9) Вызывается стандартным однообъектным выражением placement new . Стандартная реализация library не выполняет никаких действий и возвращает неизмененный ptr . Поведение не определено, если эта функция вызывается через новое выражение размещения, а ptr является указателем null.

10) Вызывается стандартным выражением массива form placement new . Стандартная реализация library не выполняет никаких действий и возвращает немодифицированный ptr . Поведение не определено, если эта функция вызывается через новое выражение размещения, а ptr является указателем null.

11) Если определено, вызывается пользовательским выражением placement new для одного объекта с соответствующей сигнатурой. Если определена специфичная для класса версия (19), она вызывается вместо (11).. Если пользователь не предоставляет ни (11), ни (19), новое выражение размещения имеет неправильный формат.

12) Если определено, вызывается выражением пользовательского массива form placement new с соответствующей сигнатурой. Если определена специфичная для класса версия (20), она вызывается вместо (12).. Если пользователь не вводит ни (12), ни (20), новое выражение размещения имеет неправильный формат.

13) Если определено, вызывается пользовательским выражением placement new для одного объекта с соответствующей сигнатурой, если требование выравнивания объекта превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Если для класса определена версия ((15) или (17)),, она вызывается вместо этого. Если не указано ни специфичное для класса, ни глобальное размещение form, учитывающее выравнивание (этот), вместо этого ищется размещение form (11), не учитывающее выравнивание.

14) Если определено, вызывается выражением пользовательского массива form placement new с соответствующей сигнатурой, если требование выравнивания элемента превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Если определена версия ((16) или (18)) для конкретного класса, вместо этого вызывается она. Если не указано ни специфичное для класса, ни глобальное размещение form, учитывающее выравнивание (этот), вместо этого ищется размещение form (12), не учитывающее выравнивание.

15) Если определено, вызывается обычным одиночным объектом new-expressions при размещении объекта типа T.

16) Если определено, вызывается обычным массивом new[]-expressions при размещении массива объектов типа T.

17) Если определено, вызывается обычным одиночным объектом new-expressions при размещении объекта типа T, если его требования к выравниванию превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Если эта перегрузка не предоставлена, но есть элемент form (15), не поддерживающий выравнивание, вместо этого вызывается перегрузка элемента, не поддерживающего выравнивание.

18) Если определено, вызывается обычным массивом new[]-expressions при выделении массива объектов типа T, если требование его выравнивания превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Если эта перегрузка не предоставлена, но есть элемент form (16), не поддерживающий выравнивание, вместо этого вызывается перегрузка элемента, не поддерживающего выравнивание.

19) Если определено, вызывается пользовательским выражением placement new для одного объекта с соответствующей сигнатурой при выделении объекта типа T.

20) Если определено, вызывается пользовательской формой массива выражения placement new[] с соответствующей сигнатурой при выделении массива объектов типа T.

21) Если определено, вызывается пользовательским выражением placement new для одного объекта с соответствующей сигнатурой при выделении объекта типа T, если его требования к выравниванию превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Если эта перегрузка не предоставлена, но есть элемент form (19), не поддерживающий выравнивание, вместо этого вызывается перегрузка элемента, не поддерживающего выравнивание.

22) Если определено, вызывается пользовательской формой массива выражения placement new[] с соответствующей сигнатурой при выделении массива объектов типа T, если его требования к выравниванию превышают __STDCPP_DEFAULT_NEW_ALIGNMENT__ . Если эта перегрузка не предоставлена, но есть форма члена, не поддерживающая выравнивание (20), вместо этого вызывается перегрузка члена, не поддерживающая выравнивание.

Parameters

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

Return value

1-4) Если выделение прошло успешно, указатель p0 , отличный от null, который указывает на соответствующим образом выровненную память размером не менее size и отличается от любого ранее возвращенного значения p1 , если только это значение p1 не было впоследствии передано заменяемому deallocation function ; если выделение не удалось, не возвращается (выдается исключение, см. below).

5-8) То же, что и ( 1-4 ), но возвращает указатель null, если выделение не удалось.

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

Exceptions

1-4) Выдает исключение типа, которое соответствовало бы обработчику типа std::bad_alloc в случае неудачного выделения памяти.

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

Global replacements

Программа имеет неправильный формат, диагностика не требуется, если в программе предусмотрено более одной замены для любой заменяемой функции распределения или если замена объявлена с помощью inline specifier . Программа имеет неправильный формат, если замена определена в пространстве имен, отличном от глобального пространства имен, или если она определена как функция, не являющаяся членом static, в глобальной области.

Стандартные реализации library версий nothrow ( 5-8 ) напрямую вызывают соответствующие версии Throw ( 1-4 ). Стандартная реализация library версий метательного массива ( 2,4 ) напрямую вызывает соответствующую версию с одним объектом ( 1,3 ). Таким образом, замены функций выделения отдельных объектов достаточно для обработки всех выделений.

Глобальная замена operator с new/delete:

#include #include #include // 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 > // нет встроенного, требуется [replacement.functions]/3 void* operator new[](std::size_t sz) < std::printf("2) new[](size_t), size = %zu\n", sz); if (sz == 0) ++sz; // избегайте std::malloc(0), который может вернуть nullptr в случае успеха if (void *ptr = std::malloc(sz)) return ptr; throw std::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), 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]; // гарантированно вызвать замену в C++11 delete[] p2; >
// Скомпилировано с GCC-5 в режиме C++17 для получения следующего: 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[] с дополнительными пользовательскими параметрами («формы размещения», версии ( 11-14 )) могут быть объявлены в глобальном масштабе, как обычно, и вызываются соответствующими формами размещения new-expressions .

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

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

Class-specific overloads

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

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

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

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

#include // специфичные для класса функции распределения 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[] с дополнительными пользовательскими параметрами («размещение forms»), также может быть определено как члены класса ( 19-22 )).). глобальную область видимости, и если указано новое размещение для класса, оно вызывается.

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

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

#include #include struct X < X() < throw std::runtime_error(""); > // пользовательское размещение новое static void* operator new(std::size_t count, bool b) < std::cout "custom placement new called, b hljs-string">'\n'; return ::operator new(count); > // пользовательское размещение удалить static void operator delete(void* ptr, bool b) < std::cout "custom placement delete called, b hljs-string">'\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 и два или более параметра. Другими словами, шаблонами могут быть только формы размещения.

Notes

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

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

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

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

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

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

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

Defect Reports

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

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

References

  • Стандарт C++23 (ISO/IEC 14882:2023):
    • 17.7 Динамическое управление памятью [support.dynamic]
    • 17.6 Динамическое управление памятью [support.dynamic]
    • 21.6 Управление динамической памятью [support.dynamic]
    • 18.6 Динамическое управление памятью [support.dynamic]
    • 18.6 Динамическое управление памятью [support.dynamic]

    New (C++)

    В языке программирования C++, new — оператор, обеспечивающий выделение динамической памяти в куче. За исключением формы, называемой «размещающей формой new», new пытается выделить достаточно памяти в куче для размещения новых данных и, в случае успеха, возвращает адрес свежевыделенной памяти. Однако, если new не может выделить память в куче, то он передаст (throw) исключение типа std::bad_alloc . Это устраняет необходимость явной проверки результата выделения.

    Синтаксис

    Синтаксис new выглядит следующим образом:

    p_var = new typename; 

    где p_var — ранее объявленный указатель типа typename . typename может подразумевать собой любой фундаментальный тип данных или объект, определенный пользователем (включая, enum , class и struct ). Если typename — это тип класса, то вызывается конструктор по умолчанию для создания объекта.

    Для инициализации новой переменной, созданной при помощи new нужно использовать следующий синтаксис:

    p_var = new type(initializer); 

    где initializer — первоначальное значение, присвоенное новой переменной, а если type — тип класса, то initializer — аргумент(ы) конструктора.

    new может также создавать массив:

    p_var = new type [size]; 

    В данном случае, size указывает размерность (длину) создаваемого одномерного массива. Адрес первого элемента возвращается и помещается в p_var , поэтому

    p_var[n] 

    означает значение n -го элемента (считая от нулевой позиции)

    Память, выделенная при помощи new , должна быть освобождена при помощи delete , дабы избежать утечки памяти. Массивы, выделенные (созданные) при помощи new[] , должны освобождаться (уничтожаться) при помощи delete[] .

    int *p_scalar = new int(5); int *p_array = new int[5]; 

    Инициализаторы не могут быть указаны для массивов, созданных при помощи new . Все элементы массива инициализируются при помощи конструктора по умолчанию для данного типа. Если тип не имеет конструктора по умолчанию, то произойдет ошибка при компиляции.

    Placement new

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

    Реализация

    В компиляторах, придерживающимся стандарта ISO C++, в случае если недостаточно памяти для выделения, то код передает исключение типа std::bad_alloc . Выполнение всего последующего кода прекращается, пока ошибка не будет обработана в блоке try-catch или произойдет экстренное завершение программы. Программа не нуждается в проверке значения указателя; если не было передано исключение, то выделение прошло успешно. Реализованные операции определяются в заголовке . В большинстве реализаций C++ оператор new также может быть перегружен для определения особого поведения.

    Освобождение динамически выделенной памяти

    Любая динамически выделенная память при помощи new должна освобождаться при помощи команды delete . Существует два варианта: один для массивов, другой — для единичных объектов.

    int *p_var = new int; int *p_array = new int[50]; delete[] p_array; delete p_var; 

    Необходимо отметить, что компилятор не требует создания диагностического сообщения при некорректном использовании delete ; он в общем случае не может знать, когда указатель указывает на одиночный элемент, а когда — на массив элементов. Более того, использование не соответствующего освобождения по сути вызывает неопределённое поведение.

    Повторное выделение памяти, выделенной при помощи new[]

    В отличие от оператора realloc в языке Си, при помощи new[] невозможно напрямую перераспределить уже выделенную память. Для увеличения или уменьшения размера блока памяти нужно выделить новый блок нужного размера, скопировать данные из старой памяти и удалить старый блок. Стандартная библиотека языка C++ предусматривает поддержку динамического массива, который может быть увеличен или уменьшен в своем шаблонном классе .

    См. также

    • Распределители памяти
    • Delete (C++)
    • Обработка исключений
    • malloc
    • Пул памяти
    • Синтаксис размещения
    • Указатель
    • Умный указатель

    Ссылки

    • Документация от IBM, описывающая оператор new языка C++ (англ.)
    • Описание оператора new применительно к Microsoft Visual Studio (англ.)

    Wikimedia Foundation . 2010 .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *