Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
A.doc
Скачиваний:
36
Добавлен:
09.04.2015
Размер:
5.6 Mб
Скачать

194 Глава 4

Операции new и delete

Предположим, что вам необходимо пространство для переменной типа double. Вы можете определить указатель на тип double и затем запрашивать память во время вы- полнения. Вы можете делать это, используя операцию new в следующих операторах:

Это хороший повод, чтобы напомнить, что все указатели должны быть инициализи- рованы. Динамическое использование памяти обычно предполагает появление множе- ства "плавающих" указателей, поэтому важно, чтобы они не содержали неправильных значений. Вы должны стараться следовать правилу, что если указатель не содержит корректного значения адреса, то он устанавливается в 0.

Операция new во второй строке приведенного выше кода должна вернуть адрес памяти из свободного хранилища, выделенный для размещения переменной типа double, и этот адрес сохраняется в pvalue. Затем вы можете применять этот указа- тель для ссылки на переменную, используя операцию разыменования, как уже было показано ранее. Например:

Конечно, может случиться, что память не будет выделена, поскольку все свобод- ное хранилище занято, или же потому, что оно настолько фрагментировано в резуль- тате предыдущего использования, что не находится непрерывного участка свободной памяти, достаточного для размещения переменной указанного типа. Однако вам не стоит слишком беспокоиться об этом. В соответствии с требованием стандарта ANSI С++, операция new возбудит исключение, если память не может быть выделена по ка- кой-либо причине, что прервет выполнение программы. Исключения — это механизм сообщения об ошибках в С++, и вы познакомитесь с ними в главе 6.

Вы можете сразу инициализировать переменную операцией new при ее создании. Если взять пример с переменной double, которая распределяется операцией new и чей адрес сохраняется в pvalue, то можно сразу установить ей значение 999.0 с по- мощью следующего оператора:

Когда отпадает необходимость в переменной, распределенной динамически, вы можете освободить память, которую она занимала в свободном хранилище, операци- ей delete:

Это обеспечивает возможность последующего использования этой памяти другими переменными. Если вы не применяете delete и позже присвоите указателю pvalue другое значение адреса, то станет невозможным освободить память или использовать переменную, которая в ней содержится, поскольку доступ к этому адресу будет утерян. В такой ситуации происходит то, что называется утечкой памяти (memory leak) — осо- бенно если данная ситуация в программе повторяется многократно.

Динамическое распределение памяти для массивов

Динамически выделить памяти для массива очень просто. Если вы хотите распре- делить массив типа char, то, предполагая, что pstr — указатель на char, можно на- писать следующий оператор:

pstr = new char[20]; // Распределение строки в 20 символов

Массивы, строки и указатели 195

Это выделяет память для символьного массива размером в 20 символов и сохраня- ет указатель на него в pstr.

Чтобы освободить распределенный таким образом массив и вернуть память в сво- бодное хранилище, вы должны применить операцию delete. Оператор должен вы- глядеть так:

Обратите внимание на квадратные скобки, свидетельствующие о том, что то, что вы удаляете — массив. Когда освобождается массив, распределенный в свободном хра- нилище, всегда нужно указывать квадратные скобки, иначе результат будет непредска- зуемым. Обратите внимание, что при этом не нужно указывать размер массива — до- статочно просто скобок [ ].

Конечно, указатель pstr после этого будет содержать адрес памяти, которая мо- жет быть уже распределена для каких-то других целей, поэтому, конечно же, он не должен никак использоваться. Поэтому когда вы применяете операцию delete для того, чтобы освободить некоторую память, которая была распределена ранее, то всег- да должны сбрасывать значение указателя в 0, как показано ниже:

pstr = 0; // Установить указатель в null

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

Ниже показан пример вывода этой программы:

Введите количество простых чисел, которые хотите получить (минимум 4): 20

Описание полученных результатов

Фактически эта программа очень похожа на предыдущую версию. После получе- ния необходимого количества простых чисел в int-переменной max, вы распределяе- те массив этого размера в свободном хранилище, используя операцию new. Обратите внимание, что программа гарантирует, что max будет не меньше 4. Это потому, что ей необходимо распределить пространство в свободном хранилище, как минимум для трех начальных простых чисел, плюс еще одно. Вы специфицируете необходимый размер массива, помещая переменную max между квадратными скобками, которые следуют за спецификацией типа массива:

pprime = new long[max];

Вы помещаете адрес области памяти, выделенной операцией new в указатель pprime. Если память не удастся выделить, то в этой точке программа должна быть прервана по исключению.

После того, как память для хранения значений простых чисел успешно выделе- на, первым трем элементам массива присваиваются значения первых трех простых чисел:

Массивы, строки и указатели 197

Вы используете операцию разыменования для доступа к первым трем элементам массива. Как было показано ранее, скобки во втором и третьем операторе необходи- мы потому, что приоритет операции * выше, чем операции +.

Вы не можете специфицировать начальные значения элементов массива, который распре- делен динамически. Если нужно установить начальные значения элементов такого массива, это следует сделать явным присваиванием.

Вычисление простых чисел выполняется точно так же, как и раньше; единствен- ное отличие в том, что вместо имени массива primes, использованного в предыдущей версии, подставляется указатель pprime. Процесс вывода — тот же. Динамическое по- лучение пространства памяти — вообще не проблема. После того, как память выделе- на, она никак не влияет на методику вычислений.

По завершении работы с массивом он освобождается операцией delete. Не за- будьте о квадратных скобках, чтобы указать, что удаляется массив.

Хотя в данном случае это и не существенно, все же указатель устанавливается в 0:

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

Динамическое распределение многомерных массивов

Выделение памяти в свободном хранилище для многомерного массива предпо- лагает использование операции new в несколько более сложной форме, чем для од- номерного массива. Если предположить, что у вас уже есть соответствующим обра- зом объявленный указатель pbeans, то получение пространства памяти для массива beans [ 3 ] [ 4 ], с которым вы имели дело ранее в этой главе, может выглядеть так:

Вы просто специфицируете оба измерения между квадратными скобками после имени типа элементов массива.

Выделение памяти для трехмерного массива просто требует указания дополни- тельного измерения с операцией new, как показано в следующем примере:

Однако сколько бы ни было измерений в созданном массиве, для того, чтобы уни- чтожить его и вернуть память обратно в свободное хранилище, потребуется написать такой оператор:

То есть при освобождении памяти массива после операции delete вы должны указать лишь одну пару квадратных скобок, независимо от того, сколько измерений было в этом массиве.

Вы уже видели, что в качестве спецификации размера при распределении памяти для одномерного массива операцией new можно указывать переменную. Это касает- ся двух и более измерений, но с тем ограничением, что переменной можно задавать лишь размер самого левого измерения.