Язык С++ поддерживает два основных типа выделения памяти:
Как статическое выделение памяти имеют два общих свойства:
В некоторых случааях этого достаточно. Но в большинстве мы заранее не можем сказать какой длины будет считанная строка, или какой длины мыссив нам нужно для наших задач. А еще большая проблема - это что хоть память нынче “дешевая”, но она все равно не бесконечна. Приведенный ниже пример просто не запустится (вероятно) у вас:
int main()
{
double m[10000000] = {}; //80 Mb
}
Почему? Потому что память для большинства обычных переменных (включая фиксированные массивы) выделяется из специального резервуара памяти — стеке (heap, не нужно путать с одноименной стрктурой памяти). Объем памяти стека в программе, как правило, не большой. Если вы превысите это значение, то произойдет переполнение стека, и операционная система автоматически завершит выполнение вашей программы.
Все эти проблемы призвано решить динамическое выделение памяти. Перед тем как рассмотртеть выделение памяти в стиле C++ рассмотрим стиль C (чтобы понимать как стало проще жить).
Стандартная библиотека cstdlib предоставляет четыре функции для управления памятью:
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
где size_t
- специальный целочисленный беззнаковый тип, может вместить в себя размер любого типа в байтах. Тип size_t
используется для указания размеров типов данных, для индексации массивов и прочее. void*
— это указатель на нетипизированную память.
malloc
- функция выделяет область памяти размера size
. Данные не инициализируютсяcallco
- выделяет массив из nmemb
размера size
. Данные инициализируются нулёмrealloc
— изменяет размер области памяти по указателю ptr
на size
(если возможно, то это делается на месте).free
— освобождает область памяти, ранее выделенную одной из функций malloc
/calloc
/realloc
. Мы всегда после выдление памяти должны ее освобождатьПример использования:
// создание массива из 1000 int
int *m = (int *)malloc(1000 * sizeof(int));
m[10] = 10;
// изменение размера массива до 2000
m = (int *)realloc(m, 2000 * sizeof(int));
// освобождение массива
free(m);
// создание массива нулей
m = (int *)calloc(3000, sizeof(int));
free(m);
m = 0;
Язык C++ предоставляет два набора операторов для выделения памяти:
new
и delete
- для одиночных значенийnew[]
и delete[]
- для массивовВерсия оператора delete
должна соответствовать версии оператора new
// выделение памяти под один int со значением 5
int *m = new int(5);
delete m; // освобождение памяти
// создание массива значений типа int
m = new int[1000];
delete[] m; // освобождение памяти
Во-первых, надо признать, что и у выделения памяти динамически есть свои недостатки:
В С/C++ разоботчик ответственен за корретное управление (выделение/удаление) памятью.
// создание массива из 1000 int
int *m = new int[1000];
// создание массива из 2000 int
m = new int[2000]; // утечка памяти
// Не вызван delete [] m, утечка памяти
int *m1 = new int[1000];
delete m1; // должно быть delete [] m1
int *p = new int(0);
free(p); // совмещение функций C++ и C
int *q1 = (int *)malloc(sizeof(int));
free(q1);
free(q1); // двойное удаление
int *q2 = (int *)malloc(sizeof(int));
free(q2);
q2 = 0; // обнуляем указатель (или nullptr)
free(q2); // правильно работает для q2 = 0