C++基础之虚析构函数原理

时间:2023-01-08 00:03:08

结论

虚函数表指针 + 虚函数表 共同实现

演示

VS2017(32位)

基类有析虚构函数

一段代码演示

#include <iostream>
#include <memory> class shape
{
public:
virtual ~shape()
{
std::cout << "~shape\n\n";
}
}; class circle : public shape
{
public:
~circle()
{
std::cout << "~circle\n\n";
}
}; int main(int argc, char *argv[], char *env[])
{
std::unique_ptr<shape> pshape(new(std::nothrow) circle); return 0;
}

circle 继承 基类shape, main函数中用一个派生类的对象赋值给基类的指针 。

内存模型

circle的内存模型如下:

1>class circle	size(4):
1> +---
1> 0 | +--- (base class shape)
1> 0 | | {vfptr}
1> | +---
1> +---
1>
1>circle::$vftable@:
1> | &circle_meta
1> | 0
1> 0 | &circle::{dtor}

基类的析构函数是虚函数,故排在最前面的是虚函数表指针,接着是基类成员,然后是派生类成员。

注意:虚函数表中,存放了 派生类的的析构函数的地址(&circle::{dtor})。

分析

当发生析构时,派生类首先调用派生类的析构函数,再调用基类的析构函数。

pshape指向的派生类的对象,正因为存在虚函数表指针,析构时,根据虚函数表指针指向虚函数表中的析构函数(&circle::{dtor}),这样,就能精准定位派生类的析构函数。

基类无析构函数的情况

既然基类没有虚析构函数,尽管基类存在虚函数,发生析构时,派生类的虚函数表中没有存放析派生类的析构函数的地址,所以不能精准定位派生类的析构函数的地址,派生类的释放可能存在内存隐患。

一段代码

相对上面的代码,基类的析构函数去掉,额外增加一个虚函数。

#include <iostream>

class shape
{
public:
~shape()
{
std::cout << "~shape\n\n";
} virtual void run(){}
}; class circle : public shape
{
public:
~circle()
{
std::cout << "~circle\n\n";
}
}; int main(int argc, char *argv[], char *env[])
{
std::unique_ptr<shape> pshape(new(std::nothrow) circle); return 0;
}

内存模型

1>class circle	size(4):
1> +---
1> 0 | +--- (base class shape)
1> 0 | | {vfptr}
1> | +---
1> +---
1>
1>circle::$vftable@:
1> | &circle_meta
1> | 0
1> 0 | &shape::run