我对大多数OOP理论都有很好的理解,但最让我困惑的是虚拟析构函数。
我以为析构函数总是被调用,不管是什么,也不管是链中的每个对象。
你打算什么时候让它们虚拟化?为什么?
我对大多数OOP理论都有很好的理解,但最让我困惑的是虚拟析构函数。
我以为析构函数总是被调用,不管是什么,也不管是链中的每个对象。
你打算什么时候让它们虚拟化?为什么?
当前回答
在多态基类中声明虚拟的析构函数。这是Scott Meyers的有效C++中的第7项。Meyers接着总结道,如果一个类有任何虚拟函数,那么它应该有一个虚拟析构函数,而不是设计为基类或不是设计为多态使用的类不应该声明虚拟析构器。
其他回答
当您希望不同的析构函数在通过基类指针删除对象时遵循正确的顺序时,析构函数的虚拟关键字是必需的。例如:
Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ;
如果基类析构函数是虚拟的,那么对象将按顺序被析构函数(首先是派生对象,然后是基)。如果基类析构函数不是虚拟的,那么只有基类对象会被删除(因为指针是基类“base*myObj”)。因此派生对象将存在内存泄漏。
还要注意,在没有虚拟析构函数时删除基类指针将导致未定义的行为。我最近学到的东西:
C++中重写删除应该如何操作?
我已经使用C++多年了,但我还是设法自杀了。
当您需要从基类调用派生类析构函数时。您需要在基类中声明虚拟基类析构函数。
我认为这个问题的核心是关于虚拟方法和多态性,而不是具体的析构函数。下面是一个更清晰的例子:
class A
{
public:
A() {}
virtual void foo()
{
cout << "This is A." << endl;
}
};
class B : public A
{
public:
B() {}
void foo()
{
cout << "This is B." << endl;
}
};
int main(int argc, char* argv[])
{
A *a = new B();
a->foo();
if(a != NULL)
delete a;
return 0;
}
将打印出:
This is B.
如果没有虚拟,它将打印出:
This is A.
现在您应该了解何时使用虚拟析构函数。
通过指向基类的指针调用析构函数
struct Base {
virtual void f() {}
virtual ~Base() {}
};
struct Derived : Base {
void f() override {}
~Derived() override {}
};
Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived
虚拟析构函数调用与任何其他虚拟函数调用都没有区别。
对于base->f(),调用将被分派到Derived::f()中,对于base->~base()也是如此-它的重写函数-将调用Derived::~Derived()。
间接调用析构函数时也会发生同样的情况,例如delete base;。delete语句将调用base->~base(),该函数将被分派到Derived::~Derived()。
具有非虚拟析构函数的抽象类
若您不打算通过指向其基类的指针删除对象,那个么就不需要使用虚拟析构函数。只需保护它,使其不会被意外调用:
// library.hpp
struct Base {
virtual void f() = 0;
protected:
~Base() = default;
};
void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.
//-------------------
// application.cpp
struct Derived : Base {
void f() override { ... }
};
int main() {
Derived derived;
CallsF(derived);
// No need for virtual destructor here as well.
}