создать и удалить (C++)
На ++ языке программирования C новый и delete — это пара языковых конструкций , которые выполняют динамическое выделение памяти , создание и уничтожение объектов . [1]
Обзор
[ редактировать ]За исключением формы, называемой «новое размещение» , Оператор new процесса обозначает запрос на выделение памяти в куче . Если доступно достаточно памяти, new инициализирует память, вызывая при необходимости конструкторы объектов, и возвращает адрес вновь выделенной и инициализированной памяти. [2] [3] А новый запрос в простейшей форме выглядит следующим образом:
p = new T;
где p — ранее объявленный указатель типа T (или какой-либо другой тип, к которому Может быть назначен указатель T , например суперкласс , Т ). Конструктор по умолчанию для T , если таковой имеется, вызывается для построения Экземпляр T в выделенном буфере памяти.
Если в свободном хранилище недостаточно памяти для объекта типа Т , новый запрос указывает на сбой, выдавая исключение типа std::bad_alloc . Это устраняет необходимость явно проверять результат выделения.
Аналог освобождения новое это delete , который сначала вызывает деструктор (если есть) для своего аргумента, а затем возвращает память, выделенную новый вернулся в бесплатный магазин. Каждый звонок в new должен соответствовать вызову удалить ; невыполнение этого требования приводит к утечке памяти . [1]
новый синтаксис имеет несколько вариантов, которые позволяют более точно контролировать выделение памяти и создание объектов. Синтаксис, подобный вызову функции, используется для вызова конструктора, отличного от конструктора по умолчанию, и передачи ему аргументов, например:
p = new T(argument);
вызывает одинаргумент Конструктор T вместо конструктора по умолчанию при инициализации вновь выделенного буфера.
Другой вариант выделяет и инициализирует массивы объектов, а не отдельные объекты:
p = new T [N];
При этом из свободного хранилища запрашивается буфер памяти, достаточно большой для хранения непрерывного массива N объектов типа T и вызывает конструктор по умолчанию для каждого элемента массива.
Память, выделенная с помощью new[] должен быть освобожден с помощью оператор delete[] , а не удалить . Использование неподходящей формы приводит к неопределенному поведению . Компиляторы C++ не обязаны генерировать диагностическое сообщение при использовании неправильной формы.
Стандарт C++11 определяет дополнительный синтаксис:
p = new T[N] {initializer1, ..., initializerN};
который инициализирует каждый п[ я ] к инициализатор i+1 .
Обработка ошибок
[ редактировать ]Если new не может найти достаточно памяти для обслуживания запроса на выделение, он может сообщить об ошибке тремя различными способами. Во-первых, стандарт ISO C++ позволяет программам регистрировать пользовательскую функцию, называемую new_handler C++ со средой выполнения ; если да, то эта функция вызывается всякий раз, когда новый сталкивается с ошибкой. new_handler может попытаться освободить больше памяти или завершить программу, если это невозможно.
Если нет new_handler установлен, вместо этого new выдает исключение типа std::bad_alloc . Таким образом, программе не нужно проверять значение возвращаемого указателя, как это принято в C ; если исключение не было создано, выделение выполнено успешно.
Третий метод обработки ошибок представлен вариантной формой new(std::nothrow) , который указывает, что исключение не должно создаваться; вместо этого нулевой указатель возвращается , сигнализирующий об ошибке выделения.
Перегрузка
[ редактировать ]The Оператор new можно перегрузить , чтобы определенные типы (классы) использовали для своих экземпляров собственные алгоритмы выделения памяти. Например, ниже приведен вариант шаблона Singleton , где первый новый вызов Singleton выделяет экземпляр, и все последующие вызовы возвращают тот же экземпляр:
#include <cstdlib>
#include <cstddef>
class Singleton {
public:
static void* operator new(std::size_t size) {
if (!instance) {
instance = std::malloc(size);
}
refcount++;
return instance;
}
static void operator delete(void*) noexcept {
if (--refcount == 0) {
std::free(instance);
instance = nullptr;
}
}
private:
static void* instance = nullptr;
static std::size_t refcount = 0;
};
Эта возможность была доступна с самого начала истории C++, хотя конкретный механизм перегрузки изменился. Он был добавлен в язык, потому что объектно-ориентированные программы C++ имели тенденцию размещать множество небольших объектов с new , который внутри использовал распределитель C (см. § Связь с malloc и free ); однако это было оптимизировано для меньшего и большего количества выделений, выполняемых типичными программами на языке C. Страуструп сообщил, что в ранних приложениях функция C malloc был «наиболее распространенным узким местом производительности в реальных системах», поскольку программы тратили на эту функцию до 50% своего времени. [4]
Отношение к malloc и бесплатному
[ редактировать ]Поскольку стандарт C++ включает в себя стандартную библиотеку C , динамического выделения памяти C процедуры маллок , каллок , перераспределить и бесплатно также доступны программистам на C++. Использование этих подпрограмм в большинстве случаев не рекомендуется, поскольку они не выполняют инициализацию и уничтожение объектов. [5] новый и delete были фактически введены в первую версию C++ (тогда называвшуюся « C с классами »), чтобы избежать необходимости инициализации объектов вручную. [4]
В отличие от процедур C, которые позволяют увеличивать или уменьшать выделенный массив с помощью realloc невозможно изменить размер буфера памяти, выделенного новый[] . Вместо этого стандартная библиотека C ++ предоставляет динамический массив (коллекцию), который можно расширять или уменьшать по своему усмотрению. std::vector класс шаблона .
Стандарт C++ не определяет никакой связи между новый / delete и процедуры распределения памяти C, но новый и delete обычно реализуются как обертки вокруг маллок и бесплатно . [6] Смешивание двух семейств операций, например, освобождение новая выделенная память или удалить malloc'd памяти вызывает неопределенное поведение и на практике может привести к различным катастрофическим результатам, таким как неспособность снять блокировки и, следовательно, взаимоблокировка . [7]
См. также
[ редактировать ]- Распределитель (C++)
- Обработка исключений
- Пул памяти
- Указатель (компьютерное программирование)
- Получение ресурсов — это инициализация (RAII)
- Умные указатели
Ссылки
[ редактировать ]- ^ Jump up to: а б Савич, Уолтер (2013). Абсолютный С++ . Пирсон. стр. 420–445. ISBN 978-0132846813 .
- ^ «Документация IBM, описывающая новый оператор C++» . Архивировано из оригинала 3 января 2013 г. Проверено 6 ноября 2013 г.
- ^ «Новая документация по оператору Microsoft Visual Studio» . Проверено 6 ноября 2013 г.
- ^ Jump up to: а б Страуструп, Бьярн (1993). История C++: 1979–1991 (PDF) . Учеб. Конференция ACM по истории языков программирования.
- ^ Мейерс, Скотт (1998). Эффективный С++ . Аддисон-Уэсли. п. 21 . ISBN 9780201924886 .
- ^ Александреску, Андрей (2001). Современный дизайн на C++: применение общих шаблонов программирования и проектирования . Аддисон-Уэсли. п. 68 .
- ^ Сикорд, Роберт К. (2013). Безопасное программирование на C и C++ . Аддисон-Уэсли. Раздел 4.4, Распространенные ошибки управления памятью C++.