Когда у класса есть конструкторы, они вызываются всегда при создании объекта класса. Конструкторы для инициализации используются достаточно часто. Достаточно часто необходима и обратная операция ("деинициализация") для удаления объекта.
Специальная член-функция, дополняющая конструктор, называется деструктором. Имя деструктора должно соответствовать имени класса с предшествующим символом ~ (тильда). Например:
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() и будет последним выполняемым кодом в программе.