协慌网

登录 贡献 社区

何时使用虚拟析构函数?

我对大多数 OO 理论有了深刻的理解,但让我困惑的一件事是虚拟析构函数。

我认为无论什么以及链中的每个对象,析构函数总是会被调用。

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

答案

当您可能通过指向基类的指针删除派生类的实例时,虚拟析构函数很有用:

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

在这里,您会注意到我没有声明 Base 的析构函数是virtual 。现在,我们来看看以下代码段:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

由于 Base 的析构函数不是virtualb是指向Derived对象的Base* ,因此delete b具有未定义的行为

[在delete b ] 中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类, 静态类型应具有虚拟析构函数或者行为未定义

在大多数实现中,对析构函数的调用将像任何非虚拟代码一样被解析,这意味着将调用基类的析构函数,但不会调用派生类的析构函数,从而导致资源泄漏。

总而言之,总是让基类的析构函数在被多态操作时是virtual

如果要防止通过基类指针删除实例,可以使基类析构函数受保护且非虚拟; 通过这样做,编译器将不允许您在基类指针上调用delete

您可以在Herb Sutter 的这篇文章中了解有关虚拟性和虚拟基类析构函数的更多信息。

虚拟构造函数是不可能的,但虚拟析构函数是可能的。让我们实验......

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

上面的代码输出如下:

Base Constructor Called
Derived constructor called
Base Destructor called

派生对象的构造遵循构造规则,但是当我们删除 “b” 指针(基指针)时,我们发现只有基本析构函数被调用。但这不能发生。要做适当的事情,我们必须使基础析构函数成为虚拟的。现在让我们看看下面发生了什么:

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

输出更改如下:

Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called

因此,基指针的破坏(在派生对象上进行分配!)遵循破坏规则,即首先导出然后是基数。另一方面,对于构造函数,没有像虚构造函数那样的东西。

在多态基类中声明析构函数是虚拟的。这是 Scott Meyers 的Effective C ++ 中的第 7 项。迈尔斯继续总结,如果一个类有任何虚函数,它应该有一个虚析构函数,而不是类设计为基类或不是设计用于多态应该声明虚析构函数。