Освобождение области динамической памяти, выделенной в строке 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.
Листинг 8.5. Размещение и удаление объектов в области динамического обмена
1: // Листинг 8.5.
2: // Размещение и удаление объектов в области динамического обмена
3:
4: #include
5:
6: class SimpleCat
7: {
8: public:
9: SimpleCat();
10: ~SimpleCat();
11: private:
12: int itsAge;
13: };
14:
15: SimpleCat::SimpleCat()
16: {
17: cout << "Constructor called.\n";
18: itsAge = 1;
19: }
20:
21: SimpleCat::~SimpleCat()
22: {
23: cout << "Destructor called.\n";
24: }
25:
26: int main()
27: {
28: cout << "SimpleCat Frisky...\n";
29: SimpleCat Frisky;
30: cout << "SimpleCat *pRags = new SimpleCat...\n";
31: SimpleCat * pRags = new SimpleCat;
32: cout << "delete pRags...\n";
33: delete pRags;
34: cout << "Exiting, watch Frisky go...\n";
35: return 0;
36: }
Результат:
SimpleCat Frisky...
Constructor called.
SimpleCat *pRags = new SimpleCat..
Constructor called.
delete pRags...
Destructor called.
Exiting, watch Frisky go...
Destructor called.
Анализ:В строках 6—13 приведено описанИе простейшего класса SimpleCat. Описание конструктора класса находится в строке 9, а его тело — в строках 15-19. Деструктор описан в строке 10, его тело — в строках 21-24.
В строке 29 создается экземпляр описанного класса, который размешается в стеке. При этом происходит неявный вызов конструктора класса SimpleCat. Второй объект класса создается в строке 31. Для его хранения динамически выделяется память и адрес записывается в указатель pRags. В этом случае также вызывается конструктор. Деструктор класса SimpleCat вызывается в строке 33 как результат применения оператора delete к указателю pRags. При выходе из функции переменная Frisky оказывается за пределами области видимости и для нее также вызывается деструктор.
Читать дальше