Например, следующий фрагмент кода определяет четыре объекта класса Sales_data:

{ // новая область видимости

 // p и p2 указывают на динамически созданные объекты Sales_data

 *p = new Sales_data;                 // p - встроенный указатель

 auto p2 = make_shared(); // p2 - shared_ptr

 Sales_data item(*p);    // конструктор копий копирует *p в item

 vector vec; // локальный объект

 vec.push_back(*p2);     // копирует объект, на который указывает p2

 delete p;               // деструктор вызывается для объекта, на

                         // который указывает p

} // выход из локальной области видимости; деструктор вызывается

  // для item, p2 и vec

  // удаление p2 уменьшает его счетчик пользователей; если значение

  // счетчика дойдет до 0, объект освобождается

  // удаление вектора vec удалит и его элементы

Каждый из этих объектов содержит член типа string, который резервирует динамическую память для содержания символов переменной-члена bookNo. Но единственная память, которой код должен управлять непосредственно, — это самостоятельно зарезервированный объект. Код непосредственно освобождает только динамически созданный объект, связанный с указателем p.

Другие объекты класса Sales_data автоматически удаляются при выходе из области видимости. По завершении блока vec, p2 и item выходят из области видимости, это означает вызов деструкторов классов vector, shared_ptr и Sales_data для соответствующих объектов. Деструктор класса vector удалит элемент, помещенный в вектор vec. Деструктор класса shared_ptr осуществит декремент счетчика ссылок объекта, на который указывает указатель p2. В данном примере этот счетчик достигнет нуля, поэтому деструктор класса shared_ptr удалит объект класса Sales_data, зарезервированный с использованием указателя p2.

Во всех случаях деструктор класса Sales_data неявно удаляет переменную-член bookNo. Удаление переменной-члена bookNo запускает деструктор класса string, который освобождает память, используемую для хранения ISBN.

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

Синтезируемый деструктор

Компилятор определяет синтезируемый деструктор (synthesized destructor) для любого класса, который не определяет собственный деструктор. Подобно конструкторам копий и операторам присвоения копии, определение для некоторых классов синтезируемого деструктора предотвращает удаление объектов этого типа (раздел 13.1.6). В противном случае у синтезируемого деструктора будет пустое тело.

Например, синтезируемый деструктор класса Sales_data эквивалентен следующему:

class Sales_data {

public:

 // не делать ничего, кроме удаления переменных-членов,

 // осуществляемого автоматически

 ~Sales_data() { }

 // другие члены как прежде

};

Переменные-члены автоматически удаляются после выполнения (пустого) тела деструктора. В частности, деструктор класса string будет выполнен для освобождения памяти, используемой переменной-членом bookNo.

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

Упражнения раздела 13.1.3

Упражнение 13.9. Что такое деструктор? Что делает синтезируемый деструктор? Когда деструктор синтезируется?

Упражнение 13.10. Что произойдет при удалении объекта класса StrBlob? А класса StrBlobPtr?

Упражнение 13.11. Добавьте деструктор в класс HasPtr из предыдущих упражнений.

Упражнение 13.12. Сколько вызовов деструктора происходит в следующем фрагменте кода?

bool fcn(const Sales_data *trans, Sales_data accum) {

 Sales_data item1(*trans), item2(accum);

 return item1.isbn() != item2.isbn();

}

Перейти на страницу:

Похожие книги