Новая версия сайта находится на стадии beta-тестирования

Урок #10

На прошлых двух уроках мы узнали что такое массивы и ссылки. Теперь настало время узнать о динамичном выделении памяти. Вы можете задаться вопросом: "А зачем нам все это, ведь мы и так нормально справляемся с обычными массивами". Но не все так просто. Допустим перед нами стоит проблема оптимизации некой программы, которая выполняет действия внутри массива (под оптимизацией подразумевается не только ускорение самого алгоритма, а и более рациональное использование памяти). Допустим предел входных данных в 10 тысяч чисел. Вот мы и создали такой огромный массив, что бы все туда влезло. Вроде бы все хорошо, но не так, как кажется. Если на вход дано всего 10 чисел, так какой смысл обрабатывать массив из 10 элементов, при этом запихнув его в массив из 10000 элементов? Никакого смысла в этом нет. Для этого и было введено динамическое выделение памяти. В языке C для динамического выделения памяти использовались функции malloc и free, которые в свою очередь выделяли и освобождали память. В C++ данные функции остались, но было введено новое и более приятное и понятное решение: операторы new и delete. Сейчас поговорим об этом подробнее.

Оператор new

Не сложно догадаться из названия данного оператора за что он отвечает. С его помощью можно выделять память. Это делается путем выделения нужного места в кучи (англ. heap), если там есть место для выделяемого участка памяти. Так как память выделяется именно там, то для выделения динамической памяти нам надо сначала создать указатель, который получит некий адрес, и с данного адреса будет выделена память нужного размера. Сперва создадим ссылку и выделим память для объекта типа int.

int main() {
    int *number;
    number = new int;
}

Как видим, ничего сложного. Сперва создается указатель, который пока указывает на null. (Круто, да? Сразу на null! Но не тут-то было! Если в режиме дебага просмотреть содержимое данной ссылки, то можно увидеть не только 0x00000000, что и является null, а и 0xDCDCDCDC, и другой мусор. Так что, если необходимо чтобы ново созданные ссылки указывали на null, необходимо сделать это вручную). Затем при помощи new выделяется память под элемент типа int и указатель получает первый адрес выделенной памяти, так как оператор new возвращает этот адрес. Занести что-то в данную память можно разыменовав указатель: *number = 10. Таким же способом можно вывести и то, что в нем хранится: cout << number << " << *number (сперва выводится адрес, а затем то, что записано данной ячейке памяти). Но есть способ использования оператора new, при котором можно сразу поместить некое значение в ячейку памяти: number = new int(10).

Чтобы выделить память под массив следует после указания типа поставить квадратные скобки и в них указать размер желанного массива. В обычных массивах их размер мы могли лишь указать либо константой, либо числовым литералом, а здесь можно даже переменными. Рассмотрим следующий пример:

int main() {
    int elements, *arr;
    cin >> elements;
    arr = new int[elements];
}

Так мы создаем массив такого размера, который нам укажет пользователь. Помните, что при создании массива мы указываем число элементов, поэтому номером последнего элемента будет elements - 1.

Также можно создавать и динамические многомерные массивы. Для примера рассмотрим прямоугольную матрицу m на n. Не сложно догадаться, что это будет динамический массив динамических массивов.

int main() {
    int **arr, m, n;
    cin >> m >> n;
    arr = new int*[m];
    for (int i = 0; i < 10; i++)
        arr[i] = new int[n];
}

Оператор delete

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

Для освобождения памяти, которую занимала переменная используется следующая конструкция: delete имя_переменной, а для массивов: delete [] имя_массива.

Возможные вопросы по теме:

Вопросов еще нет

Добавляйте свои вопросы в комментарии. Лучшие попадут в статью :)

Урок #9 Урок #11