14.11  Деструктор

Когда у класса есть конструкторы, они вызываются всегда при со­здании объекта класса. Конструкторы для инициализации исполь­зуются достаточно часто. Достаточно часто необходима и обратная операция ("деинициализация") для удаления объекта.

Специальная член-функция, дополняющая конструктор, назы­вается деструктором. Имя деструктора должно соответствовать имени класса с предшествующим символом ~ (тильда). Например:

class Stack

{

 public:

  Stack(int);             // Конструктор

 ~Stack();               // Деструктор

}

Деструктор вызывается автоматически в двух случаях:

1) когда объект, созданный объявлением, выходит из области видимости;

2) когда объект, созданный операцией new, удаляется опера­цией delete.

Обычно деструктор нужен в классе, который получает динами­ческую память, связанную с объектом. Деструктор освобождает эту динамическую память, чтобы дать возможность ее повторного использования после выхода объекта из области видимости. Например:

Stack :: ~Stack() { delete s; }      // Деструктор

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

Рассмотрим использование деструктора ~ Stack() класса Stack. При создании объекта класса Stack (объявлением или операцией new) рабо­тает конструктор Stack(), и объекту класса Stack, включающему член-данные s, max_len и top, дополнительно распределяется динамическая область памяти размера size, и ее адрес помещается в член-данные s.

При выходе объекта, созданного объявлением, из области видимости или удалении объекта (созданного по new) операцией delete автоматически освобож­дается память, связанная с член-данными s, max_len и top. Для этого никаких специальных действий не требуется. Но в этом случае динами­ческая область памяти, распределенная конструктором, автоматически не освободится, а адрес этой области, содержащийся в член-данном s, окажется "потерянным". Для таких случаев и требуется деструктор.

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

Рассмотрим пример класса Stack, содержащий конструктор и деструктор:

Пример 5

#include <iostream.h>

const int MAXSIZE = 20;

const char * msg[ ] = { "Переполнение стека n", "Стек пуст n");

enum boolean (FALSE, TRUE};       

class Stack

{

private:

char * s;

int max_len;

int top;    

public:

void Err_rep(int e_num) { cout << msg[e_num];}

Stack(int size = 512)

{

s = new char[size];

max_len = size;

top = 0;

}        // Конструктор

void Reset() { top = 0; }

const boolean Push(char c)

{

if (top = = MAXSIZE)

{

Err_rep(0); 

return 0;    

}

s[top++] = c;

return TRUE;

}

 char Pop()

{

if (top = = 0)

{

Err_rep(1);

return FALSE;

}    

return (s[top—]);     

}                

char Top()

{

return (s[top]);

}     

boolean Empty()

 {

return (top = = 0);

 }       

boolean Full()

{

return ( top = = MAXSIZE – 1);          

 }

~Stack { delete s;  }            // Деструктор

 };

При выходе объекта из области видимости деструктор не вызы­вается автоматически в следующих случаях:

1) для объектов, созданных операцией new;

2) когда из области видимости выходит указатель на объект.

Рассмотрим пример.

Пример 6

Stack s1(200);                              // Объект 1

Func()

Stack * sp1= &s1;                      // Объект 1

Stack * sp2 = new Stack(500); // Объект 2

Stack s3;                                  // Объект 3

// . . .

 }

По завершению функции Func деструктор ни для объекта, адре­суемого указателем sp1 и расположенного в статической памяти, ни для объекта, адресуемого указателем sp2 и расположенного в динамической памяти, вызываться не будет. Чтобы удалить, например, объект 2, надо перед выходом из функции Func указать:

delete sp2;      // Для операции delete автоматически вызывается деструктор.

Для объекта s3 деструктор вызывется автоматически при заверше­нии функции Func, т.е. при выходе имени s3 из области видимости.

Конструкторы для статических объектов выполняются в поряд­ке объявления объектов; деструкторы вызываются в обратном по­рядке. Например:

 Stack s1(50), s2(70);

 void Func() {

static Stack s3(100);

}     

  void main(void)

  {

Func();

}

Конструктор вызывается трижды: для s1, s2 и s3. Деструктор также вызывается трижды: для удаления s3, s2 и s1 при выходе из функции main() и завершении программы. Для локального статического объекта конструктор вызывается до вызова функции, но после вызова конструкторов для глобаль­ных статических объектов.

Обычно выполнение программы рассматривается как выполне­ние функции main(). Но в языке C++, используя конструкторы и деструк­торы для глобальных объектов, расположенных в статической па­мяти, можно выполнить определенные действия до вызова или по­сле завершения main(). Например, если объявление сделано до функции main(), конструктор выполнится до начала выполнения программы. Деструктор выполнится после завершения функции main() и будет последним выполняемым кодом в программе.