Classes and class hierarchies
C.30
Определяйте деструктор, если классу требуется явное действие при уничтожении объекта
Причина
Деструктор неявно вызывается в конце времени жизни объекта. Если деструктор по умолчанию достаточен — используйте его. Определяйте не-дефолтный деструктор только если класс должен выполнить код, который ещё не является частью деструкторов его членов.
Пример
template<typename A>
struct final_action { // слегка упрощённо
A act;
final_action(A a) : act{a} {}
~final_action() { act(); }
};
template<typename A>
final_action<A> finally(A act) // выводим тип действия
{
return final_action<A>{act};
}
void test()
{
auto act = finally([] { cout << "Exit test\n"; }); // устанавливаем действие при выходе
// ...
if (something) return; // act выполняется здесь
// ...
} // act выполняется здесь
Вся цель final_action — обеспечить выполнение некоторого кода (обычно лямбды) при уничтожении.
Примечание
Существуют два общих класса классов, которым нужен пользовательский деструктор:
- Класс с ресурсом, который ещё не представлен в виде класса с деструктором, например
vectorили класс транзакции. - Класс, существующий главным образом для выполнения действия при уничтожении, например трассировщик или
final_action.
Пример (плохой)
class Foo { // плохо; используйте деструктор по умолчанию
public:
// ...
~Foo() { s = ""; i = 0; vi.clear(); } // очистка
private:
string s;
int i;
vector<int> vi;
};
Деструктор по умолчанию справится лучше, эффективнее и не допустит ошибок.
Контроль
Ищите вероятные «неявные ресурсы», такие как указатели и ссылки. Ищите классы с деструкторами, хотя все их члены данных имеют деструкторы.