Листинг 8.4. Выделение, использование и освобождение динамической памяти



1; // Листинг 8, 4,

2; // Выделение, использование и освобождение динамической памяти 3;

4: #include <iostream.h>

5: int main()

6: {

7: int localVariable = 5;

8: int * pLocal= &localVariable;

9: int * pHeap = new int;

10: рНеар = 7;

11: cout << "localVariable: " << localVariable << "\n";

12: cout << "*pLocal: " << *pLocal << "\n";

13: cout << "*pHeap; " << *pHeap << "\n";

14: delete рНеар;

15: рНеар = new int;

16: *pHeap = 9;

17: cout << "*pHeap: " << *pHeap << "\n";

18: delete рНеар;

19: return 0;

20: }

 

Результат:

localVariable: 5

*pLocal: 5

*pHeap: 7

*pHeap: 9

 

Анализ: В строке 7 объявляется и инициализируется локальная переменная localVariable. Затем объявляется указатель, которому присваивается адрес этой переменной (строка 8). В строке 9 выделяется память для переменной типа int и адрес выделенной области помещается в указатель рНеар. Записав по адресу, содержащемуся в рНеар, значение 7, можно удостовериться в том, что память была выделена корректно (строка 10). Если бы память под переменную не была выделена, то при выполнении этой строки появилось бы сообщение об ошибке.

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

В строке 10, после выделения памяти, по адресу в указателе записывается значение 7. Затем в строках 11 и 12 значения локальной переменной и указателя pLocal выводятся на экран. Вполне понятно, почему эти значения равны. Далее, в строке 13, выводится значение, записанное по адресу, хранящемуся в указателе рНеар. Таким образом, подтверждается, что значение, присвоенное в строке 10, действительно доступно для использования.

Освобождение области динамической памяти, выделенной в строке 9, осуществляется оператором delete в строке 14. Освобожденная память становится доступной для дальнейшего использования, и ее связь с указателем разрывается. После этого указатель рНеар может использоваться для хранения нового адреса. В строках 15 и 16 выполняется повторное выделение памяти и запись значения по соответствующему адресу. Затем в строке 17 это значение выводится на экран, после чего память освобождается.

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

 

 

Что такое утечка памяти

 

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

 

1: unsigned short int * pPointer = new unsigned short int;

2: *pPointer = 72;

3: delete pPointer;

4: pPointer = new unsigned short int;

5: *pPointer = 84;

 

В строке 1 объявляется указатель и выделяется память для хранения переменной типа unsigned short int. В следующей строке в выделенную область записывается значение 72. Затем в строке 3 указателю присваивается адрес другой области памяти, в которую записывается число 84 (строка 4). После выполнения таких операций память, содержащая значение 72, оказывается недоступной, поскольку указателю на эту область было присвоено новое значение. В результате невозможно ни использовать, ни освободить зарезервированную память до завершения программы. Правильнее было бы написать следующее:

 

1: unsigned short int * pPointer = new unsigned short int;

2: *pPointer = 72;

3: pPointer = new unsigned short int;

4: *pPointer = 84;

 

В этом случае память, выделенная под переменную, корректно освобождается (строка 3).

 

Примечание: Каждый раз, когда в программе используется оператор new, за ним должен следовать оператор delete. Очень важно следить, какой указатель ссылается на выделенную область динамической памяти, и вовремя освобождать ее.

 

 

Размещение объектов в области динамической памяти

 

Аналогично созданию указателя на целочисленный тип, можно динамически размещать в памяти любые объекты. Например, если вы объявили объект класса Cat, для манипулирования этим объектом можно создать указатель, в котором будет храниться его адрес, — ситуация, абсолютно аналогичная размещению переменных в стеке. Синтаксис этой операции такой же, как и для целочисленных типов:

 

Cat *pCat = new Cat;

 

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

 

Удаление объектов

 

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


Дата добавления: 2019-02-12; просмотров: 236; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!