Я обнаружил утечку памяти в моем коде, вызванную вызовом только деструктора базового класса для объектов. Эта проблема понята: я уже добавил virtual
деструктору класса интерфейса MyÌnterface
, Меня озадачивает то, что компилятор явно создал стандартный деструктор для моего вспомогательного класса MyHelper
это в конечном итоге называется. Я попробовал это с двумя разными компиляторами.
Это меня очень удивило, так как я заметил, что большинство реализаций по умолчанию не создаются, если члены или базовые классы вводят ограничения. Почему защита деструктора не передается по наследству?
#include <iostream>
class MyInterface
{
public:
virtual void doSomethingUseful()=0;
// a lot more functions declared omitted
virtual void doSomethingElse()=0;
virtual void doSomethingIndividual()=0;
protected:
/// protected destructor to forbid calling it on interfaces
~MyInterface() {} // HERE the virtual is clearly missing
};
/// a common base that defaults most functions implementations
class MyHelper: public MyInterface
{
public:
void doSomethingUseful() {}
// a lot more default implementations omitted
void doSomethingElse() {}
};
class SomeImplementation: public MyHelper
{
public:
SomeImplementation()
{
std::cout << "SomeImplementation ctr" << std::endl;
}
~SomeImplementation()
{
std::cout << "SomeImplementation dtr" << std::endl;
}
void doSomethingIndividual()
{
std::cout << "SomeImplementation did it." << std::endl;
}
};
/// user of MyInterface cannot delete object mi passed as parameter
int deleteSafeUsage(MyInterface& mi)
{
mi.doSomethingIndividual();
// would cause a compiler error: delete &mi;
}
/// usage restricted to MyHelper level, only exception is object creation
int testIt()
{
MyHelper* h = new SomeImplementation;
deleteSafeUsage(*h);
delete h; // <- HERE the memory leak happens!
}
Вот вывод приведенного выше примера кода, который «показывает» недостающие SomeImplementation ctr
:
SomeImplementation ctr
SomeImplementation did it.
Конструкторы и деструкторы не наследуются. Так почему же их видимость наследуется?
Вы можете проверить стандарт, чтобы быть уверенным, но cppreference говорит это, акцент мой:
Если пользовательский деструктор не предоставлен для типа класса (struct, class или union), компилятор всегда объявляет деструктор как встроенный общественности член своего класса.
Итак, если вы хотите ~MyHelper
Чтобы быть защищенным, вы должны объявить это явно.
Обратите внимание, что если MyInterface
был виртуальный деструктор, неявный деструктор MyHelper
также будет виртуальным. Так что этот аспект унаследован, в некотором роде. Опять же, вы хотите обратиться к стандарту, если вы хотите быть уверенным, но это упоминается в c ++ faq lite
Для полноты вот Херб Саттерс руководство о том, как использовать виртуальность в целом и с деструкторами.
Других решений пока нет …