虚析构和纯虚析构原理

虚析构和纯虚析构原理虚析构和纯虚析构原理直接上代码:请根据编号查看代码说明。先总结:虚析构或纯虚析构就是用来解决通过父类指针释放子类对象如果子类中没有堆区数据,可以不写为虚析构或纯虚析构拥有纯虚析构函数的类也属于抽象类animal类//3.classanimal{public: animal(){cout<<“animal构造函数调用”<<endl;} //能调用子类析构函数来释放堆区的解决方法: //利用虚析构可以解决父

大家好,欢迎来到IT知识分享网。

虚析构和纯虚析构原理

直接上代码:请根据编号查看代码说明。

先总结:

  1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

  2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

  3. 拥有纯虚析构函数的类也属于抽象类

animal类

//3.
class animal
{
public:
	animal() { cout << "animal构造函数调用" << endl; }
    
	//能调用子类析构函数 来释放堆区的解决方法:
    
	//利用虚析构可以解决父类指针释放子类对象时不干净的问题
	//virtual ~animal() { cout << "animal虚析构函数调用" << endl; }

	//而如果想避免基类实例化,则可以将析构函数写成纯虚析构
	//但因为派生类(子类)不可能来实现基类(父类)的析构函数,
	//所以基类析构函数虽然可以标为纯虚,但是仍必须实现析构函数,
	//否则派生类无法继承,也无法编译通过。
    
	//纯虚析构 要声明也要实现(类外定义)
	//有了纯虚析构之后,这个类也属于抽象类,并无法实例化对象
	
	virtual  ~animal() = 0;
   
	virtual void speak() = 0; 
};

//纯虚析构函数要类外定义
animal::~animal() { cout << "animal纯虚析构函数调用" << endl; }

cat类,里面继承了父类animal

// 4.
class cat : public animal
{
public:
	cat(string name)
	{
		cout << "cat构造函数调用" << endl;
		m_Name = new string(name);
	}
    
	virtual void speak() { cout << *m_Name << "小猫在说话" << endl; }
	
	~cat()
	{
		if (m_Name != NULL)
		{
			cout << "cat析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}

	string* m_Name;
};
// 5.
void te01()
{
	//父类指针指向了一个开辟在堆区的子类数据

	animal* a = new cat("Tom");
    
	//new一个在堆区的cat类型的对象,cat继承自animal
	//我们知道创建子类时会先创建父类
    
	//所以先调用了animal的构造函数
	//再是cat的构造函数
   
    
	//亿点细节:
    
		//在创建父类时,如果发现父类中有虚函数
		//编译器就会使用一个叫虚函数表的东西,保存所有的虚函数,包括纯虚函数
		//在这里,animal父类的虚函数表中,有两个虚函数
    
		/*
			class animal	size(4):
					+---
			0		| (vfptr)
					+---
					
			animal::$vftable@:
					| &animal_mata
					|  0
			0		| &animal::(dtor) 
			1		| &animal::speak
		*/
    
		//&animal::(dtor)  就是animal的纯虚析构函数的地址
		//dtor是destructor的缩写,是析构函数的意思,中文直译叫"垃圾焚毁炉"
		//&animal::speak   就是speak虚函数的地址
    
		//接着,子类继承了这个表,重写了speak。

		/*
			class cat       size(8):
			        	+---
			 0      	| +--- (base class animal)
			 0      	| | {vfptr}
			        	| +---
			 4      	| m_Name
			        	+---
			        
			cat::$vftable@:
					| &cat_meta
					|  0
					| &cat::(dtor)
			 0		| &cat::speak
		*/
    
    //这个dtor我不知道算不算重写,我觉的应该是的
    //但我不了解原理,所以搬了一个别人的解释:
    
    //当父类析构函数定义为虚函数后,子类默认就是虚析构函数,跟普通成员函数一样
    //现在将父类析构函数 定义为虚函数,子类再继承了 父类的虚函数表
    //子类的虚函数表中,就存在父类的 虚析构函数的地址,
    //但子类的析构函数 也是虚函数,所以重写了自己虚函数表中 父类虚析构函数的地址
    //变成了子类的虚析构函数地址
    //虽然父子的析构函数名字不一样,但是他们占同一个坑
    //(即父子析构函数在虚函数表中的位置是一样的,否则就不存在多态了)
    
    //用父类指针指向子类,并最后通过父类指针删除子类对象的时候,
    //发现animal类的析构函数是虚函数,就到子类对象的虚函数表里找析构函数
    //此时就会调用虚函表中 子类的虚析构函数 并很好的防止了内存泄漏
    
    //然后还有个问题,
    //既然子类的虚函数表中只有子类虚析构函数,那它怎样析构父类呢?
    //父类的析构函数又在哪呢?
    
    //其实在子类的析构函数中,底层包含着对父类析构函数的调用
    
    //原因我认为是:
    //对象都存放在栈中,创建子类对象先调用父类的对象,
    //父类对象压入栈,然后是子类对象入栈,由于栈是先进后出,
    //delete父类的时候就先调用子类对象,在调用父类对象
    
    
	
    a->speak();
    //因为子类的speak重写了父类的speak,父类指针调用的是子类的speak 
    
    delete a;
    //父类指针在析构的时候 不会调用子类中的析构函数
    //导致子类如果有堆区属性,出现内存泄漏
    
    //原因是指针a是animal类型的指针,也就是父类类型的指针
    //释放a时只进行animal类的析构函数。
    
    //但父类析构函数变成虚析构之后,这个问题就解决了
    
}
// 1.
int main()
{
    // 2.
	te01();
	return 0;
}

本人才疏学浅,若有疏漏和错误,请指正 2892870137@qq.com
欢迎拜访我的主博客:https://h-james.github.io/

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/10818.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信