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

结论

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

演示

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
posted @ 2020-12-22 12:57  mohist  阅读(624)  评论(0编辑  收藏  举报