Динамическая память (dynamic memory) — область памяти, где объекты создаются
выражением new и живут, пока их явно не удалят через delete. Время жизни
такого объекта не привязано ни к блоку, ни ко всей программе — им управляет
программист.
Зачем нужно
Кроме динамической, в C++ есть глобальная (статическая) и автоматическая (стековая) области, а также потоковая, которую в курсе не разбирают. У глобальной и стековой есть ограничения:
- фиксированное время жизни: глобальные переменные живут до конца программы, локальные — до конца текущего блока (см. Область действия);
- размер: как правило, эти области ограничены несколькими мегабайтами, а реальные программы часто требуют гораздо больше.
Динамическая память снимает оба ограничения. Цена — освобождать её нужно самостоятельно.
Как работает
new
new T, new T(...), new T[...] создают в динамической области объект типа
T и возвращают указатель на него. Имени у объекта нет —
обращаются к нему только через указатель:
int* pa = new int; // без инициализации
int* pb = new int(11); // инициализация числом 11Форма с квадратными скобками создаёт массив — см. Динамический массив.
Объект живёт с момента new независимо от блоков, поэтому его можно создать
внутри блока и пользоваться им снаружи:
int* p = nullptr;
{
p = new int(13);
}
std::cout << *p; // Okdelete
delete <указатель> удаляет объект и возвращает память системе:
int* p = new int;
// ...
delete p;- удалять можно только память, выделенную через
new; иначеdelete— UB; deleteнулевого указателя — корректная операция.
Утечка памяти
Утечка памяти (memory leak) — неконтролируемый рост лишней памяти: объект больше не нужен, а удалить его нельзя, потому что указатель на него потерян или о нём забыли.
int* p = new int;
p = nullptr; // потеряли указатель — утечка
new float; // выделили память, указатель не сохранили
p = new int; // забыли удалить после использования — утечкаДве классические ошибки
| Ошибка | Название | Средство |
|---|---|---|
обратились к объекту после delete | use after free | не обращаться после delete |
не вызвали delete | memory leak | ownership и RAII |
Первая порождает висячий указатель. Владение (ownership) и RAII (resource acquisition is initialization) на лекции L03 только названы.
Кто освобождает
Каждый объект из new удаляется ровно один раз: ноль раз — утечка, два —
двойное удаление. Правило из лекции:
- кто выделил, тот и освобождает;
- если только он не передал владение другому;
- если другой сделал себе копию, он освобождает её сам.
Объект, созданный без new, удалять через delete нельзя — он уничтожится
сам на выходе из блока.
malloc и operator new
В C память выделяли функцией malloc. В отличие от new, она возвращает
void*, требует число байт и не инициализирует память:
int* p = static_cast<int*>(std::malloc(sizeof(int)));
*p = 0;
std::free(p);В C++ аналог malloc/free — функции operator new и operator delete.
Используются они редко, основной способ — выражения new/delete, которые
внутри себя эти функции и вызывают.
Тонкости
Не из лекции
- «Куча» (heap) — распространённое неформальное название динамической области; в лонгриде его нет.
- Пары не смешиваются:
malloc— сfree,new— сdelete,new[]— сdelete[]. Заголовок дляmalloc/free—<cstdlib>.- Утечки ловит санитайзер:
g++ -g -fsanitize=addressдля кода выше печатает при выходеERROR: LeakSanitizer: detected memory leaks(GCC 16.2). См. Компиляция и сборка.
Частые ошибки
- Потерять единственный указатель на объект — переприсвоить его или выйти из блока, где он объявлен.
- Удалить объект дважды: и в вызывающей, и в вызываемой функции.
- Вызвать
deleteдля адреса обычной переменной. - Читать память, полученную
new intилиmalloc, до первой записи: она не инициализирована.
Углубление — L04
Форма new T[n] создаёт в динамической памяти массив из n элементов и
возвращает указатель на нулевой. В отличие от обычного массива, n не обязан
быть константой времени компиляции. Память из new[] освобождается только
через delete[]:
int* array = new int[10];
// ...
delete[] array;Подробно, вместе с многомерными динамическими массивами, — в Динамический массив.