создать и удалить (C++)

Ключевые слова программирования C++ для динамического выделения памяти

В языке программирования C++ new и delete представляют собой пару языковых конструкций , которые выполняют динамическое выделение памяти , создание объекта и уничтожение объекта . [1]

Обзор

За исключением формы, называемой "placement new" , оператор new обозначает запрос на выделение памяти в куче процесса . Если памяти достаточно, new инициализирует память, вызывая при необходимости конструкторы объектов, и возвращает адрес в недавно выделенную и инициализированную память. [2] [3] Запрос new в простейшей форме выглядит следующим образом:

p = новый T ;   

где p — ранее объявленный указатель типа T (или какой-либо другой тип, которому может быть назначен указатель T , например, суперкласс T ) . Конструктор по умолчанию для T , если таковой имеется, вызывается для создания экземпляра T в выделенном буфере памяти.

Если в свободном хранилище недостаточно памяти для объекта типа T , новый запрос указывает на сбой, выдавая исключение типа std::bad_alloc . Это устраняет необходимость явной проверки результата выделения.

Аналогом освобождения памяти для new является delete , который сначала вызывает деструктор (если таковой имеется) для своего аргумента, а затем возвращает память, выделенную new, обратно в свободное хранилище. Каждый вызов new должен быть сопоставлен с вызовом delete ; невыполнение этого требования приводит к утечке памяти . [1]

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

p = новый T ( аргумент );   

вызывает конструктор T с одним аргументом вместо конструктора по умолчанию при инициализации вновь выделенного буфера.

Другой вариант выделяет и инициализирует массивы объектов, а не отдельные объекты:

p = новый T [ N ];    

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

Память, выделенная с помощью new[], должна быть освобождена оператором delete[] , а не delete . Использование неподходящей формы приводит к неопределенному поведению . Компиляторы C++ не обязаны генерировать диагностическое сообщение при использовании неправильной формы.

Стандарт C++11 определяет дополнительный синтаксис,

p = новый T [ N ] { инициализатор1 , ..., инициализаторN };      

который инициализирует каждый p[ i ] инициализатором i+1 .

Обработка ошибок

Если new не может найти достаточно памяти для обслуживания запроса на выделение, он может сообщить об ошибке тремя различными способами. Во-первых, стандарт ISO C++ позволяет программам регистрировать пользовательскую функцию, называемую new_handler, в среде выполнения C++ ; если это так, то эта функция вызывается всякий раз, когда new сталкивается с ошибкой. New_handler может попытаться сделать больше памяти доступной или завершить программу, если не может.

Если new_handler не установлен, new вместо этого выдает исключение типа std::bad_alloc . Таким образом, программе не нужно проверять значение возвращаемого указателя, как это принято в C ; если исключение не было выдано, выделение прошло успешно.

Третий метод обработки ошибок предоставляется вариантной формой new(std::nothrow) , которая указывает, что не следует выдавать никаких исключений; вместо этого возвращается нулевой указатель , сигнализирующий об ошибке выделения памяти.

Перегрузка

Оператор new может быть перегружен, так что определенные типы (классы) используют пользовательские алгоритмы выделения памяти для своих экземпляров. Например, ниже приведен вариант шаблона singleton , где первый новый вызов Singleton выделяет экземпляр, а все последующие вызовы возвращают этот же экземпляр:

#include <cstdlib> #include <cstddef> класс Синглтон {  общественность : статический void * оператор новый ( std :: size_t размер ) {      если ( ! экземпляр ) {   экземпляр = std :: malloc ( размер );   } refcount ++ ; возвратить экземпляр ;  } статический оператор void удалить ( void * ) noexcept {      если ( -- refcount == 0 ) {     std :: free ( экземпляр ); экземпляр = nullptr ;   } }частный : статический void * экземпляр = nullptr ;     статический std :: size_t refcount = 0 ;    };

Эта функция была доступна с самого начала истории C++, хотя конкретный механизм перегрузки изменился. Она была добавлена ​​в язык, поскольку объектно-ориентированные программы C++ имели тенденцию выделять много небольших объектов с помощью new , который внутренне использовал распределитель C (см. § Связь с malloc и free); который, однако, был оптимизирован для меньшего количества и больших выделений, выполняемых типичными программами C. Страуструп сообщил, что в ранних приложениях функция C malloc была «наиболее распространенным узким местом производительности в реальных системах», при этом программы тратили до 50% своего времени на эту функцию. [4]

Связь с malloc и free

Поскольку стандарт C++ включает в себя стандартную библиотеку C , процедуры динамического выделения памяти C malloc , calloc , realloc и free также доступны программистам C++. Использование этих процедур не рекомендуется для большинства случаев, поскольку они не выполняют инициализацию и уничтожение объектов. [5] new и delete были, по сути, введены в первой версии C++ (тогда называвшейся « C with Classes »), чтобы избежать необходимости ручной инициализации объектов. [4]

В отличие от процедур C, которые позволяют увеличивать или уменьшать выделенный массив с помощью realloc , невозможно изменить размер буфера памяти, выделенного с помощью new[] . Вместо этого стандартная библиотека C++ предоставляет динамический массив (коллекцию), который можно расширить или уменьшить в своем классе-шаблоне std::vector .

Стандарт C++ не определяет никакой связи между new / delete и процедурами выделения памяти C, но new и delete обычно реализуются как оболочки вокруг malloc и free . [6] Смешение двух семейств операций, например, освобождение только что выделенной памяти или удаление malloc - отведенной памяти, вызывает неопределенное поведение и на практике может привести к различным катастрофическим результатам, таким как невозможность снять блокировки и , следовательно, к взаимоблокировке . [7]

Смотрите также

Ссылки

  1. ^ ab Savitch, Walter (2013). Absolute C++ . Pearson. стр.  420–445 . ISBN 978-0132846813.
  2. ^ "Документация IBM, описывающая оператор new в C++". Архивировано из оригинала 2013-01-03 . Получено 2013-11-06 .
  3. ^ "Новая документация по операторам Microsoft Visual Studio" . Получено 2013-11-06 .
  4. ^ ab Stroustrup, Bjarne (1993). История C++: 1979–1991 (PDF) . Proc. ACM History of Programming Languages ​​Conf.
  5. ^ Мейерс, Скотт (1998). Эффективный C++ . Эддисон-Уэсли. стр. 21. ISBN 9780201924886.
  6. ^ Александреску, Андрей (2001). Современный дизайн C++: применение шаблонов обобщенного программирования и проектирования . Addison-Wesley. стр. 68.
  7. ^ Seacord, Robert C. (2013). Безопасное кодирование на C и C++ . Addison-Wesley.Раздел 4.4, Распространенные ошибки управления памятью в C++.
Получено с "https://en.wikipedia.org/w/index.php?title=Новый_и_удаленный_(C%2B%2B)&oldid=1272550028#void.2A_operator_новый.28размер_t_размер.29"