首页 > 代码库 > 从为什么要将基类的析构函数定义为虚函数谈起~~

从为什么要将基类的析构函数定义为虚函数谈起~~

 首先,做一个最简单的实验,在电脑上运行下面的代码,将会产生运行错误,这或许会使你百思不得其解:

#include <iostream>
using namespace std;

class Base
{
private:
    int a;
public:
    ~Base(){cout << "Base dtor..." << endl;}
};

class Derived : public Base
{
private:
    int b;
public:
     ~Derived(){cout << "Derived dtor..." << endl;}
};
int main()
{
    Base *b = new Base();
    Base *d = new Derived();
    delete b;
    delete d; //这一步出现了运行时错误
    return 0;
}
显然,这里出现了内存泄露。


记得刚开始学继承的时候,似乎就被强行的记住 “基类的析构函数要是虚函数!!”,可是为什么呢?

显然,这与对象在内存中的布局方式有关。


在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。


值得注意的是,无论继承(单层)层次有多深,对象中都只有一个虚函数指针。。

也就是说,如果Derived2 继承 Derived ,最终的内存布局中也只有一个虚函数指针。

那么上述类在内存中的存在方式如下图:

显然,这可以很好的解释 析构函数 是virtual的例子了,也就是说,如果析构函数不是virtual,那么在

Base *d = new Derived();该d对象所指向的是derived的虚函数表(因为虚函数表在对象内存的起始位置),这样便可以通过Base的指针找到derived对象的析构函数,从而在 delete  d ; 的时候可以释放Derived对象,不会导致内存泄露。


事实上,只需要将继承层次的最顶端的父类的析构函数设为虚函数,就可以避免上述问题,因为基类的析构函数是虚的,那么它的子类的析构函数也是虚的。


拓展:如果在基类中还存在其他的虚函数,很简单,直接将该虚函数存放在虚函数列表中,如果子类继承了改虚函数,那么只需要将子类对象的虚函数表中的函数替换为子类定义的版本即可。没有继承的,则沿用父类的版本。


从为什么要将基类的析构函数定义为虚函数谈起~~