Если класс имеет владеющий указатель-член, определите деструктор
Причина
Объект, которым владеют, должен быть delete-нут при уничтожении владеющего объекта.
Пример
Указатель-член может представлять ресурс. T* не должен это делать, но в старом коде это распространено. Рассматривайте T* как возможного владельца и относитесь к нему с подозрением.
template<typename T>
class Smart_ptr {
T* p; // ПЛОХО: неясно владение *p
// ...
public:
// ... нет пользовательских операций по умолчанию ...
};
void use(Smart_ptr<int> p1)
{
// ошибка: p2.p утечёт (если не nullptr и не принадлежит другому коду)
auto p2 = p1;
}
Обратите внимание: если вы определяете деструктор, вы должны определить или удалить все операции по умолчанию:
template<typename T>
class Smart_ptr2 {
T* p; // ПЛОХО: неясно владение *p
// ...
public:
// ... нет пользовательских операций копирования ...
~Smart_ptr2() { delete p; } // p — владелец!
};
void use(Smart_ptr2<int> p1)
{
auto p2 = p1; // ошибка: двойное удаление
}
Операция копирования по умолчанию просто скопирует p1.p в p2.p, что приведёт к двойному уничтожению p1.p. Будьте явными в отношении владения:
template<typename T>
class Smart_ptr3 {
owner<T*> p; // OK: явно указано владение *p
// ...
public:
// ...
// ... операции копирования и перемещения ...
~Smart_ptr3() { delete p; }
};
void use(Smart_ptr3<int> p1)
{
auto p2 = p1; // OK: нет двойного удаления
}
Примечание
Зачастую простейший способ получить деструктор — заменить указатель умным указателем (например, std::unique_ptr) и позволить компилятору организовать корректное уничтожение неявно.
Примечание
Почему бы не потребовать, чтобы все владеющие указатели были «умными»? Это иногда потребовало бы нетривиальных изменений кода и могло бы повлиять на ABI.
Контроль
- Класс с указателем в качестве члена данных подозрителен.
- Класс с
owner<T>должен определять свои операции по умолчанию.