clean_up()
; люди просто забудут ее вызвать. Мы можем предложить более удачное решение. Основная идея состоит в том, чтобы компилятор знал не только о конструкторе, но и о функции, играющей роль, противоположную конструктору. Такую функцию логично назвать
// очень упрощенный вектор, содержащий числа типа double
class vector {
int sz; // размер
double* elem; // указатель на элементы
public:
vector(int s) // конструктор
:sz(s), elem(new double[s]) // выделяет память
{
for (int i=0; i
// элементы
}
~vector() // деструктор
{ delete[] elem; } // освобождаем память
// ...
};
Итак, теперь можно написать следующий код:
void f3(int n)
{
double* p = new double[n]; // выделяем память для n
// чисел типа double
vector v(n); // определяем вектор (выделяем память
// для других n чисел типа double)
// ...используем p и v...
delete[ ] p; // освобождаем память, занятую массивом
// чисел типа double
} // класс vector автоматически освободит
// память, занятую объектом v
Оказывается, оператор delete[]
такой скучный и подвержен ошибкам! Имея класс vector
, нет необходимости ни выделять память с помощью оператора new
, ни освобождать ее с помощью оператора delete[]
при выходе из функции. Все это намного лучше сделает класс vector
. В частности, класс vector
никогда не забудет вызвать деструктор, чтобы освободить память, занятую его элементами.
iostream
? Они очищали буферы, закрывали файлы, освобождали память и т.д. Все это делали их деструкторы. Каждый класс, “владеющий” какими-то ресурсами, должен иметь деструктор.
17.5.1. Обобщенные указатели
Если член класса имеет деструктор, то этот деструктор будет вызываться при уничтожении объекта, содержащего этот член. Рассмотрим пример.
struct Customer {
string name;
vector
// ...
};
void some_fct()
{
Customer fred;
// инициализация объекта fred
// использование объекта fred
}
Когда мы выйдем из функции some_fct()
и объект fred
покинет свою область видимости, он будет уничтожен; иначе говоря, будут вызваны деструкторы для строки name и вектора addresses
. Это совершенно необходимо, поскольку иначе могут возникнуть проблемы. Иногда это выражают таким образом: компилятор сгенерировал деструктор для класса Customer
, который вызывает деструкторы членов. Такая генерация выполняется компилятором часто и позволяет гарантированно вызывать деструкторы членов класса.
Деструкторы для членов — и для базовых классов — неявно вызываются из деструктора производного класса (либо определенного пользователем, либо сгенерированного). По существу, все правила сводятся к одному: деструкторы вызываются тогда, когда объект уничтожается (при выходе из области видимости, при выполнении оператора delete
и т.д.).
17.5.2. Деструкторы и свободная память
• Если функции в качестве ресурса требуется какой-то объект, она обращается к конструктору.
• На протяжении своего срока существования объект может освобождать ресурсы и запрашивать новые.