|
Программирование >> Инициализация объектов класса, структура
int ival = 0; создаем объект типа int и инициазируем его 0 приблизительно эквивалентен следующей последовательности инструкций: int *pi = &ival; указатель сс1лается на этот объект не считая, конечно, того, что объект, адресуемый pi, создается библиотечной функцией new() и размещается в хипе. Аналогично iStack *ps = new iStack( 512 ); создает объект типа iStack на 512 элементов. В случае объекта класса значение или значения в скобках передаются соответствующему конструктору, который вызывается в случае успешного выделения памяти. (Динамическое создание объектов классов более подробно рассматривается в разделе 15.8. Оставшаяся часть данного раздела посвящена созданию объектов встроенных типов.) Описанные операторы new могут вызывать одну проблему: хип, к сожалению, является конечным ресурсом, и в некоторой точке выполнения программы мы можем исчерпать его. Если функция new() не может выделить затребованного количества памяти, она возбуждает исключение bad alloc. (Обработка исключений рассматривается в главе 11.) Время жизни объекта, на который указывает pi, заканчивается при освобождении памяти, где этот объект размещен. Это происходит, когда pi передается оператору delete. Например, delete pi; освобождает память, на которую ссылается pi, завершая время жизни объекта типа int. Программист управляет окончанием жизни объекта, используя оператор delete в нужном месте программы. Этот оператор вызывает библиотечную функцию delete() , которая возвращает выделенную память в хип. Поскольку хип конечен, очень важно возвращать ее своевременно. Глядя на предыдущий пример, вы можете спросить: а что случится, если значение pi по необходимо ли это? if ( pi != 0 ) какой-либо причине было нулевым? Не следует ли переписать этот код таким образом: delete pi; Нет. Язык С++ гарантирует, что оператор delete не будет вызывать функцию delete() в случае нулевого операнда. Следовательно, проверка на 0 необязательна. (Если вы явно добавите такую проверку, в большинстве реализаций она фактически будет выполнена дважды.) Важно понимать разницу между временем жизни указателя pi и объекта, который он адресует. Сам объект pi является глобальным и объявлен в глобальной области Оператор new выполняет следующую последовательность действий: выделяет из хипа память для объекта, затем инициализирует его значением, стоящим в скобках. Для выделения памяти вызывается библиотечная функция new() . Предыдущий оператор void f() { int i; string str = dwarves ; int *pi = &i; short *ps = 0; double *pd = new doub1e(33); delete str; плохо: str не является динамическим объектом delete pi; плохо: pi ссылается на локальный объект delete ps; безопасно delete pd; безопасно Ниже приведены примеры опасных и безопасных операторов delete: Вот три основные ошибки, связанные с динамическим выделением памяти: не освободить выделенную намять. В таком случае намять не возвращается в хип. Эта ошибка получила название утечки памяти; дважды применить оператор delete к одной и той же области памяти. Такое бывает, когда два указателя получают адрес одного и того же динамически размещенного объекта. В результате подобной ошибки мы вполне можем удалить нужный объект. Действительно, намять, освобожденная с помощью одного из адресующих ее указателей, возвращается в хип и затем выделяется под другой объект. Затем оператор delete применяется ко второму указателю, адресовавшему старый объект, а удаляется при этом новый; изменять объект после его удаления. Такое часто случается, поскольку указатель, к которому применяется оператор delete, не обнуляется. Эти ошибки при работе с динамически выделяемой памятью гораздо легче допустить, нежели обнаружить и исправить. Для того чтобы помочь программисту, стандартная библиотека С++ представляет класс auto ptr. Mi рассмотрим его в следующем подразделе. После этого мы покажем, как динамически размещать и уничтожать массивы, используя вторую форму операторов new и delete. видимости. Следовательно, память под него выделяется до выполнения программы и сохраняется за ним до ее завершения. Совсем не так определяется время жизни адресуемого указателем pi объекта, который создается с помощью оператора new во время выполнения. Область памяти, на которую указывает pi, выделена динамически, следовательно, pi является указателем на динамически размещенный объект типа int. Когда в программе встретится оператор delete, эта память будет освобождена. Однако память, отведенная самому указателю pi, не освобождается, а ее содержимое не изменяется. После выполнения delete объект pi становится висячим указателем, то есть ссылается на область памяти, не принадлежащую программе. Такой указатель служит источником трудно обнаруживаемых ошибок, поэтому сразу после уничтожения объекта ему полезно присвоить 0, обозначив таким образом, что указатель больше ни на что не ссылается. Оператор delete может использоваться только по отношению к указателю, который содержит адрес области памяти, выделенной в результате выполнения оператора new. Попытка применить delete к указателю, не ссылающемуся на такую память, приведет к непредсказуемому поведению программы. Однако, как было сказано выше, этот оператор можно применять к нулевому указателю. auto ptr< type pointed to > identifier( ptr allocated by new ); auto ptr< type pointed to > identifier( auto ptr of same type ); Определение объекта auto ptr имеет три формы: auto ptr< type pointed to > identifier; Здесь type pointed to представляет собой тип нужного объекта. Рассмотрим последовательно каждое из этих определений. Как правило, мы хотим непосредственно инициализировать объект auto ptr адресом объекта, созданного с помощью оператора new. Это можно сделать следующим образом: auto ptr< int > pi ( new int( 1024 ) ); В результате значением pi является адрес созданного объекта, инициализированного числом 1024. С объектом, на который указывает auto ptr, можно работать обычным if ( *pi != 1024 ) ошибка, что-то не так способом: else *pi *= 2; Объект, на который указывает pi, будет автоматически уничтожен по окончании времени жизни pi. Если указатель pi является локальным, то объект, который он адресует, будет уничтожен при выходе из блока, где он определен. Если же pi глобальный, то объект, на который он ссылается, уничтожается при выходе из программы. Что будет, если мы инициализируем auto ptr адресом объекта класса, скажем, auto ptr< string > стандартного класса string? Например: 8.4.2. Шаблон auto ptr А В стандартной библиотеке С++ auto ptr является шаблоном класса, призванным помочь программистам в манипулировании объектами, которые создаются посредством оператора new. (К сожалению, подобного шаблона для манипулирования динамическими массивами нет. Использовать auto ptr для создания массивов нельзя, это приведет к непредсказуемым результатам.) Объект auto ptr инициализируется адресом динамического объекта, созданного с помощью оператора new. Такой объект автоматически уничтожается, когда заканчивается время жизни auto ptr. В этом подразделе мы расскажем, как ассоциировать auto ptr с динамически размещаемыми объектами. Для использования шаблона класса auto ptr необходимо включить заголовочный файл: #include <memory>
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |