我对大多数OOP理论都有很好的理解,但最让我困惑的是虚拟析构函数。

我以为析构函数总是被调用,不管是什么,也不管是链中的每个对象。

你打算什么时候让它们虚拟化?为什么?


当前回答

当您希望不同的析构函数在通过基类指针删除对象时遵循正确的顺序时,析构函数的虚拟关键字是必需的。例如:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

如果基类析构函数是虚拟的,那么对象将按顺序被析构函数(首先是派生对象,然后是基)。如果基类析构函数不是虚拟的,那么只有基类对象会被删除(因为指针是基类“base*myObj”)。因此派生对象将存在内存泄漏。

其他回答

我认为这个问题的核心是关于虚拟方法和多态性,而不是具体的析构函数。下面是一个更清晰的例子:

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.

现在您应该了解何时使用虚拟析构函数。

虚拟基类析构函数是“最佳实践”——您应该始终使用它们来避免(难以检测)内存泄漏。使用它们,可以确保类的继承链中的所有析构函数都被调用(按正确的顺序)。使用虚拟析构函数从基类继承也会使继承类的析构函数自动虚拟化(因此不必在继承类析构函数声明中重新键入“virtual”)。

关于virtual的一个基本定义是它确定类的成员函数是否可以在其派生类中被覆盖。

类的D-tor基本上在作用域的末尾被调用,但存在一个问题,例如,当我们在堆(动态分配)上定义一个实例时,我们应该手动删除它。

一旦指令被执行,就会调用基类析构函数,但不会调用派生的析构函数。

一个实际的例子是,在控制场中,你必须操纵效应器和致动器。

在范围结束时,如果没有调用其中一个动力元件(执行器)的析构函数,将产生致命的后果。

#include <iostream>

class Mother{

public:

    Mother(){

          std::cout<<"Mother Ctor"<<std::endl;
    }

    virtual~Mother(){

        std::cout<<"Mother D-tor"<<std::endl;
    }


};

class Child: public Mother{

    public:

    Child(){

        std::cout<<"Child C-tor"<<std::endl;
    }

    ~Child(){

         std::cout<<"Child D-tor"<<std::endl;
    }
};

int main()
{

    Mother *c = new Child();
    delete c;

    return 0;
}

还要注意,在没有虚拟析构函数时删除基类指针将导致未定义的行为。我最近学到的东西:

C++中重写删除应该如何操作?

我已经使用C++多年了,但我还是设法自杀了。

在多态基类中声明虚拟的析构函数。这是Scott Meyers的有效C++中的第7项。Meyers接着总结道,如果一个类有任何虚拟函数,那么它应该有一个虚拟析构函数,而不是设计为基类或不是设计为多态使用的类不应该声明虚拟析构器。