我知道在C++中为基类声明虚析构函数是一种良好的编程实践,但即使作为接口的抽象类,也有必要声明virtual
析构函数吗?请提供一些原因和示例。
我知道在C++中为基类声明虚析构函数是一种良好的编程实践,但即使作为接口的抽象类,也有必要声明virtual
析构函数吗?请提供一些原因和示例。
对于一个接口来说,这一点更加重要。你的类的任何用户可能会持有指向接口而非具体实现的指针。当他们试图删除它时,如果析构函数不是虚函数,它们将调用接口的析构函数(或者如果你没有指定默认值,则调用编译器提供的默认值),而不是派生类的析构函数。这将导致内存泄漏。
例如
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
我决定进行一些研究并尝试总结您的答案。以下问题将帮助您决定需要哪种析构函数:
希望这可以帮到您。
*重要的是要注意,在C++中没有标记类为final(即不可子类化)的方法,因此如果您决定将析构函数声明为非虚和公共的,则请明确警告您的同事不要从您的类派生。
参考文献:
是的,这非常重要。派生类可能会分配内存或持有对其他资源的引用,在对象销毁时需要清理这些资源。如果您不为接口/抽象类提供虚拟析构函数,则每次通过基类句柄删除派生类实例时,都不会调用派生类的析构函数。
因此,您会打开潜在的内存泄漏风险。
class IFoo
{
public:
virtual void DoFoo() = 0;
};
class Bar : public IFoo
{
char* dooby = NULL;
public:
virtual void DoFoo() { dooby = new char[10]; }
void ~Bar() { delete [] dooby; }
};
IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
这并非总是必须的,但我认为这是一种好的实践方法。它的作用是允许通过基类型的指针安全地删除派生对象。
例如:
Base *p = new Derived;
// use p as you see fit
delete p;
这不仅是良好的实践,而且是任何类层次结构的第一规则。
现在来说为什么。以典型的动物层次结构为例。虚析构函数通过虚分派和其他方法调用一样。看以下示例。
Animal* pAnimal = GetAnimal();
delete pAnimal;
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
您可能更喜欢上面的删除方式,但如果基类的析构函数不是虚函数,则只会调用基类的析构函数,派生类中的所有数据都将保持未删除状态。
delete p
调用未定义的行为。不能保证调用Interface::~Interface
。 - Mankarse[expr.delete]/
规定:“如果要删除的对象的静态类型与其动态类型不同,则静态类型必须具有虚析构函数,否则行为未定义......”,所以这段代码是未定义的。即使派生类使用了隐式生成的析构函数,情况仍然是如此。 - MankarseInterface
类中添加virtual ~Interface()
。 - snibbe