- •6 Глава 1
- •12 Глава 1
- •14 Глава 1
- •16 Глава 1
- •18 Глава 1
- •20 Глава 1
- •22 Глава 1
- •24 Глава 1
- •26 Глава 1
- •31 Глава 1
- •34 Глава 2
- •36 Глава 2
- •Puc. 2.4. Дополнительные опции консольного приложения Win32
- •38 Глава 2
- •40 Глава 2
- •42 Глава 2
- •44 Глава 2
- •48 Глава 2
- •50 Глава 2
- •52 Глава 2
- •54 Глава 2
- •56 Глава 2
- •58 Глава 2
- •60 Глава 2
- •62 Глава 2
- •64 Глава 2
- •66 Глава 2
- •68 Глава 2
- •70 Глава 2
- •74 Глава 2
- •76 Глава 2
- •79 Глава 2
- •82 Глава 2
- •84 Глава 2
- •86 Глава 2
- •88 Глава 2
- •92 Глава 2
- •94 Глава 2
- •96 Глава 2
- •98 Глава 2
- •103 Глава 2
- •105 Глава 2
- •107 Глава 2
- •110 Глава 2
- •115 Глава 3
- •119 Глава 3
- •121 Глава 3
- •123 Глава 3
- •125 Глава 3
- •129 Глава 3
- •131 Глава 3
- •133 Глава 3
- •139 Глава 3
- •141 Глава 3
- •143 Глава 3
- •145 Глава 3
- •148 Глава 3
- •150 Глава 3
- •155 Глава 3
- •165 Глава 4
- •168 Глава 4
- •170 Глава 4
- •173 Глава 4
- •175 Глава 4
- •178 Глава 4
- •184 Глава 4
- •186 Глава 4
- •188 Глава 4
- •190 Глава 4
- •192 Глава 4
- •194 Глава 4
- •198 Глава 4
- •201 Глава 5
- •203 Глава 5
- •205 Глава 5
- •207 Глава 5
- •213 Глава 5
- •217 Глава 5
- •219 Глава 5
- •221 Глава 5
- •223 Глава 5
- •225 Глава 5
- •227 Глава 5
- •232 Глава 5
- •234 Глава 5
- •236 Глава 5
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 символов
Массивы,
строки
и
указатели
Это выделяет память для символьного массива размером в 20 символов и сохраня- ет указатель на него в pstr.
Чтобы освободить распределенный таким образом массив и вернуть память в сво- бодное хранилище, вы должны применить операцию delete. Оператор должен вы- глядеть так:
Обратите внимание на квадратные скобки, свидетельствующие о том, что то, что вы удаляете — массив. Когда освобождается массив, распределенный в свободном хра- нилище, всегда нужно указывать квадратные скобки, иначе результат будет непредска- зуемым. Обратите внимание, что при этом не нужно указывать размер массива — до- статочно просто скобок [ ].
Конечно, указатель pstr после этого будет содержать адрес памяти, которая мо- жет быть уже распределена для каких-то других целей, поэтому, конечно же, он не должен никак использоваться. Поэтому когда вы применяете операцию delete для того, чтобы освободить некоторую память, которая была распределена ранее, то всег- да должны сбрасывать значение указателя в 0, как показано ниже:
pstr = 0; // Установить указатель в null
Чтобы увидеть на практике, как работает динамическое выделение памяти, вы мо- жете переписать программу вычисления простых чисел, используя память из свобод- ного хранилища для их размещения.
Ниже показан пример вывода этой программы:
Введите количество простых чисел, которые хотите получить (минимум 4): 20
Описание полученных результатов
Фактически эта программа очень похожа на предыдущую версию. После получе- ния необходимого количества простых чисел в int-переменной max, вы распределяе- те массив этого размера в свободном хранилище, используя операцию new. Обратите внимание, что программа гарантирует, что max будет не меньше 4. Это потому, что ей необходимо распределить пространство в свободном хранилище, как минимум для трех начальных простых чисел, плюс еще одно. Вы специфицируете необходимый размер массива, помещая переменную max между квадратными скобками, которые следуют за спецификацией типа массива:
pprime = new long[max];
Вы помещаете адрес области памяти, выделенной операцией new в указатель pprime. Если память не удастся выделить, то в этой точке программа должна быть прервана по исключению.
После того, как память для хранения значений простых чисел успешно выделе- на, первым трем элементам массива присваиваются значения первых трех простых чисел:
Массивы,
строки
и
указатели
Вы используете операцию разыменования для доступа к первым трем элементам массива. Как было показано ранее, скобки во втором и третьем операторе необходи- мы потому, что приоритет операции * выше, чем операции +.
Вы не можете специфицировать начальные значения элементов массива, который распре- делен динамически. Если нужно установить начальные значения элементов такого массива, это следует сделать явным присваиванием.
Вычисление простых чисел выполняется точно так же, как и раньше; единствен- ное отличие в том, что вместо имени массива primes, использованного в предыдущей версии, подставляется указатель pprime. Процесс вывода — тот же. Динамическое по- лучение пространства памяти — вообще не проблема. После того, как память выделе- на, она никак не влияет на методику вычислений.
По завершении работы с массивом он освобождается операцией delete. Не за- будьте о квадратных скобках, чтобы указать, что удаляется массив.
Хотя в данном случае это и не существенно, все же указатель устанавливается в 0:
Вся память, выделенная из свободного хранилища, освобождается по завершении программы, но лучше выработать привычку сбрасывать значение указателя в ноль, когда он уже не указывает на корректную область памяти.
Динамическое распределение многомерных массивов
Выделение памяти в свободном хранилище для многомерного массива предпо- лагает использование операции new в несколько более сложной форме, чем для од- номерного массива. Если предположить, что у вас уже есть соответствующим обра- зом объявленный указатель pbeans, то получение пространства памяти для массива beans [ 3 ] [ 4 ], с которым вы имели дело ранее в этой главе, может выглядеть так:
Вы просто специфицируете оба измерения между квадратными скобками после имени типа элементов массива.
Выделение памяти для трехмерного массива просто требует указания дополни- тельного измерения с операцией new, как показано в следующем примере:
Однако сколько бы ни было измерений в созданном массиве, для того, чтобы уни- чтожить его и вернуть память обратно в свободное хранилище, потребуется написать такой оператор:
То есть при освобождении памяти массива после операции delete вы должны указать лишь одну пару квадратных скобок, независимо от того, сколько измерений было в этом массиве.
Вы уже видели, что в качестве спецификации размера при распределении памяти для одномерного массива операцией new можно указывать переменную. Это касает- ся двух и более измерений, но с тем ограничением, что переменной можно задавать лишь размер самого левого измерения.