C++57个入门知识点_46 虚析构函数的作用(当类之间存在继承关系时,最后做析构时有可能析构不完全,父子类中同时使用虚析构函数,通过查询虚表就可以解决这样的问题)

2023-11-10

今天我们只讨论一个话题,为什么析构函数最好是虚函数?

总结: 当类之间存在继承关系时,最后做析构时有可能析构不完全,父子类中同时使用虚析构函数就可以解决这样的问题

在大家在VS(vs2017中未见)创建类的时候会看到有“虚析构函数”的选项,当其打上勾之后,生成的类的析构函数时一个虚函数,本篇将会介绍析构函数使用virtual关键字的原因。
在这里插入图片描述

1. 普通写法下对象的构造与析构

对以下代码,使用普通的写法创建CTest2的对象CTest2 t;其析构是否为虚析构函数得到的结果是一样的

#include <iostream>

class CParent {
public:
	CParent() {
		printf("CParent::CParent()\r\n");
	}
	~CParent() {
		printf("CParent::~CParent()\r\n");
	}
};
class CTest2 :public CParent
{
public:
	CTest2() {
		printf("CTest2::CTest2()\r\n");
	}
	~CTest2() {
		printf("CTest2::~CTest2()\r\n");
	}
};

int main(int argc, char* argv[])
{
	CTest2 t;

	return 0;
}

运行结果:可以正常实现构造与析构
在这里插入图片描述
将子类父类的析构函数均加virtual关键字变为虚析构函数,得到的结果与上面一致

2. 儿子对象指针赋值给父亲时的析构

  • 当使用CParent* p = new CTest2;将儿子对象指针赋值给父亲,父子类的析构函数均不为虚,使用delete p;进行析构
#include <iostream>

class CParent {
public:
	CParent() {
		printf("CParent::CParent()\r\n");
	}
	 ~CParent() {
		printf("CParent::~CParent()\r\n");
	}
};
class CTest2 :public CParent
{
public:
	CTest2() {
		printf("CTest2::CTest2()\r\n");
	}
	 ~CTest2() {
		printf("CTest2::~CTest2()\r\n");
	}
};

int main(int argc, char* argv[])
{
	//创建了儿子对象,并将其指针赋值给父亲
	CParent* p = new CTest2;//儿子的内容包含父亲所有内容,指针访问安全
	delete p;

	return 0;
}

运行结果:少了一次子类CTest2的析构
在这里插入图片描述
出现上面结果的原因是deleteCParent类型(this->delete),delete p;等价于(先调用析构p指向的对象的析构函数,再释放资源):p->~CParent(); free(p),显然无法调用子类的析构。

  • 通过给析构函数加virtual关键字就可以调用到子类的析构

通过给父子类的析构函数都加virtual关键字,可以使得两个类都有虚表,其第0项为类对应的析构函数。
参考C++57个入门知识点_33_深入理解虚函数的原理-重点(间接调用:先查虚表地址,再查虚表中的虚函数指针;编译器先取对象的前4个字节地址,再取对应地址下函数指针;查看内存、反汇编的方法;成员函数指针)C++57个入门知识点_37 虚函数的直接调用与间接调用(函数的调用分为直接调用和间接调用,间接调用是虚函数所具有的的性质;间接调用:运行期通过查找对象的虚表下标来调用函数的方法)


#include <iostream>

class CParent {
public:
	CParent() {
		printf("CParent::CParent()\r\n");
	}
	virtual ~CParent() {
		printf("CParent::~CParent()\r\n");
	}
};
class CTest2 :public CParent
{
public:
	CTest2() {
		printf("CTest2::CTest2()\r\n");
	}
	virtual ~CTest2() {
		printf("CTest2::~CTest2()\r\n");
	}
};

int main(int argc, char* argv[])
{
	//创建了儿子对象,并将其指针赋值给父亲
	CParent* p = new CTest2;//儿子的内容包含父亲所有内容,指针访问安全
	delete p;

	return 0;
}

运行结果:父类子类对象的析构函数均被调用
在这里插入图片描述
上述结果也可以从p->~CParent(); free(p)进行理解,p为指针,~CParent()为虚函数,因此为间接调用,就会查子类虚表,子类虚表第0项–子类的析构,即pCTest2类型,就会调用 ~CTest2() 。子类析构调用之后再调用父类析构,再调用父类的虚表。

一般情况是否有虚并不重要,一旦有父子的继承关系,调用delete的时候,调用对应的析构函数,就会通过查询虚表进行调用,避免了某一次析构少调用的情况

3.学习视频地址: C++57个入门知识点_46 虚析构函数的作用

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++57个入门知识点_46 虚析构函数的作用(当类之间存在继承关系时,最后做析构时有可能析构不完全,父子类中同时使用虚析构函数,通过查询虚表就可以解决这样的问题) 的相关文章

随机推荐