Г. Шилдт - Полный справочник по C++ (1109478), страница 58
Текст из файла (страница 58)
Динамическое распределение памяти представляет собой важнузо часть практически всех реальных программ. Как указывалось в первои части книги. в языке С++ есть альтернативный способ диналги ~еского распределения памяти, основанный на функциях ее1ъос( > и ккее О. Они сохранены для того, пабы достичь совместимости с языком С. Однако в программах на языке С+о следует применять операторы еее и Оезеее, поскольку они имеют ряд важных преимуществ.
Оператор петг выделяет область памяти и возвращает указатель на ес первую ячейку. Оператор аеъеее освобождает память, выделенную ранее с помощью оператора пеле Общий вид этих операторов ~акое. Глава 13. Массивы, указатели, ссылки и операторы дяиамичвского распределения памяти 29$ указатьзь = пеи гния; г)е1еге указаяилги Здесь указателю присваивается адрес первой ячейки выделенной области памяпп размер которой определяется гяияаль Поскольку обьем кучи ограничен, память может оказаться исчерпанной. В этом случае оператор пеи сгенерирует исключительную ситуацию Зхьй а11ос, определенную в заголовке <пеи>. Программа должна перехватить зту ситуацию и обработать ее.
(Обработка исключительных ситуаций рассматривается в главе 19.) Если в программе не предусмотрена обработка исключительной ситуации пад е11ос, се выполнение будет прервано. Действие оператора пеи в исключительных ситуациях определено стандартом языка С++. Проблема заключается в том, что не все компиляторы полностью соответствуют стандарту. В ранних версиях языка С++ оператор пеи в случае отказа возвращал нулевой указатель.
Позднее он стач генерировать исключительную ситуацию. В иго~с было решено, что по умолчанию оператор пеи должен генерировать исключительную ситуацикк однако возвращать нулевой указатель также не запрещается. Следовательно„в кахс(ом конкретном случае поведение оператора леи регламентируется разработчиками компилятора. Разумеется, в конце концов все компиляторы будут вынуждены придерживаться стандарта, но пока следует ориентироваться на их документацию. Все примеры, описанные в нашей книге„написаны в соответствии со стандартом. Если ваш компилятор не полностью соответствует стандарту, программы следует немного изменить.
Рассмотрим пример, в котором вьиеляется динамическая память для целочисленной переменной. $1пс1ис)е <зозегеаю> Мзпс1ог)е <пеи> сззпд паиезрасе зсс); (пе юазп() ( ьпс *р; ету ( р = пеи зпс; // Вь(делить память для Целочисленной пеРеменной ) снес)ч (Ьаг) а11ос ха) ( соус « "Исключительная ситуация1п*; гегцгп 1; ) *р = 100; соса « "По адресу " « р « соце « "записано значение " « 'р « "~п"; бе1есе рг гесихп 0; ) Эта программа присваивает переменной р адрес динамической памяти, в которой может храниться целочисленная переменная. Затем в эту область памяти записывается число )ОО, а ее содержимое выводится на экран. В заключение программа освобгокдает выделенную память. У пите, если ваш компилятор реализует оператор пеи так, по в случае отказа он возврац(ает нулевой указатель, эту программу придется модифицировать.
Чае)ь Я. Язык С++ Оператор в)е1еее следует применять только к резупьтату Оператора пем. В противном случае могут возникнуть проблемы, например, крах операционной системы. Операторы пем и «те1еее аналогичны функциям еа11ос() и ггее() „но по сравнению с ними обладают несколькими преимушествами. Во-первых, оператор пем автоматически выделяет количество памяти, необходимое объекту указанного типа Оператор е1веоб теперь применять нс следует. Поскольку размер выделяемой памяти вычисляется автоматически, ошибки исключены. Во-вторых, оператор пем автоматически возвращает указатель заданного типа.
Теперь не нужно выполнять приведение типа, как это было при использовании функции еа11ое(). Операторы пезв и де1еее можно перегружатгь что позволяет создавать настраиваемые системы для выделения динамической памяти. Хотя формальных правил на этот счет не сушествует„операторы пем и вте1еее не следует смешивать с функциями еа11осО и вгееО в рамках одной и той же программы, поскольку они могут оказаться несовместимыми. Инициализация выделяемой памяти Выделяемую память можно проинициализировать заданным значением.
Для этого следует указать начальное значение после имени типа в операторе пек< Общий вид оператора пем в этом случае вьилядит следующим образом. В указатель = пеи тн~ (начальное значеннв) Разумеется, тип начального значения должен быть совместимым с типом данных, для которых выделяется память. Рассмотрим программу, которая записывает в выделенную память число 87. Ядпс1ис)е <ьозсгеаи> Еьпс1иое <пень иагпо пазьеарасе ее<)," 1пе ьзаьп() ( ьпе *р; егу ( р = пеи 1пг (87); // Инициализируем числом 87 ) саес)з (Ьас) а11ос ха) ( соил « "Исключительная ситуаиияЖ"; гееигп 1; ) соил « По адресу " сс р « соил « "записано число " « *р « "чп"; йе1еее р; гееигп О: ) Выделение памяти для массивов С помощью оператора пем можно выделять память для массивов. В этом случае оператор зм имеет следующий внд.
В указатель = пеи тан массово(размер); Здесь размер задает количество элементов размел(аемого массива. Глава 13. Массивы, указатели, ссылки и операторы динамического распределения памяти вру7 Для освобождения памяти, занятой массивом, применяется следующая форма оператора с(е1есе: я г)е1есе [) указатель; Например, следующая програмьча выделяет память для целочисленного массива. состоящего из 10 элементов.
Мъпс1иде <ъовггеазл> З1пс1иде <пеи> цвъпд паюеврасе все; ъпс юа[п() ( ъпг "р, 1; сгу ( р = пои упс [10); // вьлеляем память для целочисленного // массива, состояюего ив 10 элементов ) сасс)з (Ьа<( в11ос ха) соил « "исключительная ситуация1п') гегигп 1; лог(1=0; 1<10; 1++ ) р[1) = 1з тот(1=0; 1<10; 1+ь) соил « р[1) « се1есе [) р; // Освобождаем память, занятую массивом гесцгп О) Обратите внимание на оператор Де1еае. Как уже указывалось, при освобождении памяти, выделенной для массива с помощью оператора пем. размер массива нс указывается. (Как будет показано в следующем разделе, это свойство особенно важно при работе с массивами объектов.) Вь[деление памяти для объектов Используя оператор пем, можно динамичсски выделять память для объектов. В этом случае оператор всрнст указатслы~а созданный объект. Динамически созданный объект ничем пс отличается от других.
При его создании также вызывается конструктор (ссли он предусмотрен), а при освобождении памяти вызывается соответствующий деструктор. Рассмотрим небольшую программу, в которой определен класс Ьв1апсе, предназначенный для хранения имени человека и суммы, лежащей на сто банковском счету.
Внутри функции юадп() объект класса Ьа1епсе создастся динамически. $1пс1це)е <1овсгеаю> Мтпс1цпе <пем> Мъпс1цде <свсгзлд> цв1лп паюеврасе всд) с1авв Ьа1апсе ( ЙоцЬ1е сцг Ьа1) спал паве[80)) Часть И. язык Сз+ риЫус: уоао яес(г)оные и, сьат *я) ( сег Ьа1 = и; астору(пате, я); ) уоьй дее Ьа1(<)ооые Ьп, с)'аю *я) и = сот Ьа1; яетсру(я, пате); ) ); ьпе льзьи () ( Ьа1апсе *р; слал я(80]; г)оиые и; сту ( р = пеи Ьа1апсе; ) сассЬ (Ьас а11ос ха)( соус « "Исключительная ситуация1п'г тесолп 1; ) р->вес(12387.87, "Ральф Уилсон" ); р->дес Ьа1(п, я); сапе « я « ": сумма †.
" « и; соие « "1и г с)е1есе р; теситп 0; ) Поскольку переменная р является указателем на объект, для доступа к его членам используется оператор "->". Как известно, динамические объекты могут содержать конструкторы и деструктор.
Кроме того, конструкторы могут иметь параметры. Рассмотрим модиФикацию предыдугцей программы. Етпс1исе <зояетеат> Оьпс1исе <пеи> Оьпс1иое <сяетьпд> ияьпд патеярасе ясс(; с1аяя Ьа1апсе г)опые сь с Ьа1," онат пате[80]; рпы1 с: Ьа1апсе(с(оиЬ1е и, онат *я) ( сит Ьа1 = и; астору(пале, я); ) Глава )3. Массивы, указатели, ссылки и операторы динамического распределения памати ЯУУ -Ьа1впсе () ( соис « "Уничтожение объекта соис « папе « "1п"г ) иозс) дех Ьа1(с)оиЬ1е Ьп, спах *в) ( и = сих Ьа1; вххсру(в, папе); 1пх лга1п() ( Ьа1апсе *р; спах в(80]; боиЬ1е п; уу В втой версии используется инициализация с)" ( р = пеи Ьа1апсе (12387.87, 'Ральф„уилсон") ) сассЬ (Ьаб а11ос ха) ( соис с< "Исключительная ситуаиия1п"г хехихп 1; ) р †>дех Ьа1(п, в) соих « в « ": сумма = " « и; соис « "1п" г с)е1ехе р; хесихп 0; ) Обратите внимание на то, что параметры конструктора объекта указаны после имени типа, как при обычной инициализации.