Ответ 1
Деструктор базового класса должен быть либо открытым, либо виртуальным, либо защищенным и не виртуальным.
Начиная использовать PC-Lint на существующей базе кода (страх и трепет).
Одна вещь, на которую он жалуется, следующая:
class IBatch
{
public:
virtual void StartBatch() =0;
virtual int CommitBatch() =0;
};
Что, когда другой класс вытекает из этого, чтобы использовать его как интерфейс
base class 'IBatch' has no destructor
Итак, вопрос: когда вы создаете классы интерфейса, подобные выше, вы всегда включаете виртуальный деструктор? Зачем? (это стиль или ошибка кодирования?)
EDIT: Должен был сказать, что я не ожидаю или не хочу, чтобы пользователь IBatch разрушал, они являются потребителем только сервиса, через этот интерфейс к некоторому внешнему классу реализации (если бы это имело бы значение)
Деструктор базового класса должен быть либо открытым, либо виртуальным, либо защищенным и не виртуальным.
Ошибка кодирования. Деструктор для вашего производного класса никогда не будет вызван, если вызван через указатель на базовый класс.
Когда вы реализуете IBatch
и ссылаетесь на свой производный класс указателем на базовый класс (указатель на IBatch
), и вы вызываете delete
на этом указателе на базовый класс, вы можете потерять память, потому что деструктор для вашего производного класса никогда не будет вызван.
Основное правило: когда класс имеет хотя бы один метод virtual
, он должен иметь деструктор virtual
.
class IBatch
{
public:
virtual void f() = 0;
};
class A : public IBatch
{
public:
void f() {}
~A() {}
};
IBatch * a = new A();
a->f(); // calls A::f()
delete a; // calls IBatch::~IBatch() not A::~A()
Если есть виртуальные функции, должен быть виртуальный деструктор. Всегда. Не имеет значения, что это только класс интерфейса - ему все еще нужен виртуальный деструктор.
Либо это, либо ему нужен protected
невиртуальный деструктор. Но тогда вы не можете удалить объект с помощью указателя интерфейса.
Класс с виртуальными функциями, но не виртуальный деструктор, является подозрительным и, скорее всего, неправильным: см. хорошее и более точное объяснение здесь.
Итак, вопрос: когда вы создаете Интерфейсные классы, подобные описанным выше, делают вы всегда включаете виртуальную деструктор? Зачем? (это стиль или ошибка кодирования?)
Ну, это действительно так. Если вы когда-либо вызываете delete на указателе IBatch, он, вероятно, не будет делать то, что вы ожидаете. Конечно, если у вас есть что-то вроде виртуального Init/Shutdowns или AddRef/Releases, то это не проблема.
Компилятор ставит деструктор по умолчанию, который не является виртуальным, что подразумевает, что "удаление" на указателе на виртуальный базовый класс будет успешным с результирующей утечкой памяти. Таким образом, это ошибка реализации, ни стиль, ни ошибка кодирования.