我对大多数OO理论都有扎实的了解,但有一件事让我很困惑,那就是虚拟析构器。
我以为,无论如何,对于链中的每个对象,析构器总是被调用。
你什么时候要把它们变成虚拟的,为什么?
当你可能通过一个指向基类的指针来删除一个派生类的实例时,虚拟析构器很有用。
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
在这里,你会注意到我没有声明Base的析构函数是 "虚拟 "的。现在,让我们看一下下面的代码。
Base *b = new Derived();
// use b
delete b; // Here's the problem!
由于Base的析构器不是 "虚拟 "的,而且b
是一个指向衍生'对象的
Base*,所以
delete b`有未定义的行为。
[在
delete b
中,如果要删除的对象的静态类型为 要删除的对象的静态类型与动态类型不同,静态 类型应是要删除的对象的动态类型的基类。 删除的对象的动态类型的基类,并且静态类型应该有一个虚拟的析构器,否则 行为是未定义的。
在大多数实现中,对析构器的调用将像任何非虚拟代码一样被解决,这意味着基类的析构器将被调用,但派生类的析构器不会被调用,从而导致资源泄露。
总而言之,当基类要被多态地操作时,一定要让基类的析构器成为 "虚拟 "的。
如果你想防止通过基类指针删除一个实例,你可以使基类的析构器成为受保护的非虚拟的;通过这样做,编译器不会让你在基类指针上调用delete
。
你可以在Herb Sutter的这篇文章中了解更多关于虚拟性和虚拟基类析构器的信息。
在多态基类中宣布析构器为虚拟。 这是Scott Meyers' Effective C++中的第7条。 Meyers继续总结说,如果一个类有个虚拟函数,它就应该有一个虚拟的析构器,而不是被设计成基类或不是被设计成多态使用的类不应该声明虚拟析构器。