Kev*_*vin 159 c++ inheritance virtual-destructor
我知道在C++中为基类声明虚拟析构函数是一个好习惯,但是virtual
即使对于作为接口的抽象类来说,声明析构函数总是很重要吗?请提供一些理由和示例原因.
Air*_*Ltd 187
这对于界面来说更为重要.您的类的任何用户都可能拥有指向接口的指针,而不是指向具体实现的指针.当他们删除它时,如果析构函数是非虚拟的,它们将调用接口的析构函数(或编译器提供的默认值,如果你没有指定),而不是派生类的析构函数.即时内存泄漏.
例如
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;
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*itb 37
您的问题的答案通常是,但并非总是如此.如果您的抽象类禁止客户端在指向它的指针上调用delete(或者如果它在其文档中这样说),则可以自由地声明虚拟析构函数.
您可以禁止客户端通过使其析构函数受到保护来调用指向它的指针上的delete.像这样工作,省略虚拟析构函数是非常安全和合理的.
您最终将没有虚拟方法表,并最终通过指向它的方式向您的客户发出使其无法删除的意图,因此您确实有理由在这些情况下不将其声明为虚拟.
[见本文第4项:http://www.gotw.ca/publications/mill18.htm ]
dav*_*dag 23
我决定做一些研究并尝试总结你的答案.以下问题将帮助您确定所需的析构函数类型:
我希望这有帮助.
*重要的是要注意,在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
Run Code Online (Sandbox Code Playgroud)
它并不总是必需的,但我发现这是一种很好的做法.它的作用是允许通过基类型的指针安全地删除派生对象.
例如:
Base *p = new Derived;
// use p as you see fit
delete p;
Run Code Online (Sandbox Code Playgroud)
如果Base没有虚拟析构函数,则格式错误,因为它会尝试删除对象,就像它是a一样Base
.
这不仅是良好的做法.任何类层次结构都是规则#1.
现在为什么.采取典型的动物等级.虚拟析构函数就像任何其他方法调用一样进行虚拟调度.以下面的例子为例.
Animal* pAnimal = GetAnimal();
delete pAnimal;
Run Code Online (Sandbox Code Playgroud)
假设Animal是一个抽象类.C++知道要调用的正确析构函数的唯一方法是通过虚方法调度.如果析构函数不是虚拟的,那么它只会调用Animal的析构函数而不会破坏派生类中的任何对象.
在基类中使析构函数成为虚拟的原因是它只是从派生类中删除了选择.他们的析构函数默认变为虚拟.