C++析构函数继承时的二三事

公司项目需要,原本一直从事C开发的我,强扭着写起了C++。

一直以来C++的基础还停留在学生时代,

由于理论基础的不扎实,在项目中析构函数多重集成后犯了一个低级的理论错误,哭特此记录,引以为戒。

废话少说,看图说话。

首先定义了一个类,然后继承他,

#include <iostream>

class CPU
{
public:
	CPU(){ std::cout << "Struct CPU" << std::endl; }
	~CPU(){ std::cout << "Detruct CPU" << std::endl; }
};

class MultiCoreCPU : public CPU
{
public:
	MultiCoreCPU(){ std::cout << "Struct MultiCoreCPU" << std::endl; }
	~MultiCoreCPU(){ std::cout << "Detruct MultiCoreCPU" << std::endl; }
};

int main()
{
	CPU *a = (CPU *)(new MultiCoreCPU);
	delete a;
}

运行结果:

Struct CPU
Struct MultiCoreCPU
Detruct CPU


MultiCoreCPU的析构函数没有调用了!!

上网找了一下C++基础理论看看,这是C++继承的特性,果然既是优势,又是陷阱啊!!


C++的派生机制是,当调用类析构函数时,会从继承类的整个家族中,从第一个不是虚析构的孙子开始,一直调到祖宗的析构函数去。

这个案例中,由于祖宗CPU的析构不是虚函数,因此就会从他开始调,忽略了他的孙子MultiCoreCPU。

若要C++别落下孙子MultiCoreCPU的析构,

应该再孙子的析构函数前也加入虚函数定义。


#include <iostream>

class CPU
{
public:
	CPU(){ std::cout << "Struct CPU" << std::endl; }
	virtual ~CPU(){ std::cout << "Detruct CPU" << std::endl; } // 注意这里
};

class MultiCoreCPU : public CPU
{
public:
	MultiCoreCPU(){ std::cout << "Struct MultiCoreCPU" << std::endl; }
	virtual ~MultiCoreCPU(){ std::cout << "Detruct MultiCoreCPU" << std::endl; } // 为了避免龟孙子又有孩子,出现同样的继承问题,一同加上
};

int main()
{
	CPU *a = (CPU *)(new MultiCoreCPU);
	delete a;
}

好了,这个是我的问题的起因。

单纯这个问题要找出来也不难,我的问题更加的隐蔽。

当超过两重继承的时候,很容易就会掉以轻心了。


#include <iostream>

class CPU
{
public:
	CPU(){ std::cout << "Struct CPU" << std::endl; }
//	virtual ~CPU(){ std::cout << "Detruct CPU" << std::endl; } // 祖宗没有显式定义析构函数
};

class MultiCoreCPU : public CPU
{
public:
	MultiCoreCPU(){ std::cout << "Struct MultiCoreCPU" << std::endl; }
	virtual ~MultiCoreCPU(){ std::cout << "Detruct MultiCoreCPU" << std::endl; }
};

class IntelI5 : public MultiCoreCPU
{
public:
	IntelI5(){ std::cout << "Struct IntelI5" << std::endl; }
	virtual ~IntelI5(){ std::cout << "Detruct IntelI5" << std::endl; }
};

int main()
{
	CPU *a = (CPU *)(new IntelI5);
	delete a;
}

这个时候,由于祖宗没有显式地定义虚析构函数,因此编译器不会把他定义为虚函数,

结果就悲剧了,哭孙子里面整个家族都没有调用析构函数。


运行结果:

Struct CPU
Struct MultiCoreCPU
Struct IntelI5


刚好在项目中各个构造函数中都用到了堆的内存申请,然后就是一大堆的内存泄漏,BIEBIEBEIBE。






文末介绍一个单体的用法,

有些时候,程序定义一个类,全局只能存在一个对象,不能存在多个,

这个时候通常会用静态指针去实现,

而这个类又是继承过来的,怎么让他的祖宗也不让定义呢?把所有的构造函数定义成保护类型!!


#include <iostream>

class CPU
{
protected:
	CPU(){ std::cout << "Struct CPU" << std::endl; }

public:
	virtual ~CPU(){ std::cout << "Detruct CPU" << std::endl; }
};

class MultiCoreCPU : public CPU
{
protected:
	MultiCoreCPU(){ std::cout << "Struct MultiCoreCPU" << std::endl; }

public:
	virtual ~MultiCoreCPU(){ std::cout << "Detruct MultiCoreCPU" << std::endl; }
};

static void *
_singleton = NULL;

class IntelI5 : public MultiCoreCPU
{

protected:
	IntelI5(){ std::cout << "Struct IntelI5" << std::endl; }

public:
	virtual ~IntelI5(){ std::cout << "Detruct IntelI5" << std::endl; }

public:
	static IntelI5 *
	Singleton(){
		if(!_singleton)
		{
			_singleton = (void *)(new IntelI5);
		}
		return (IntelI5 *)_singleton;
	}
};

int main()
{
	CPU *a = (CPU *)(IntelI5::Singleton());
	delete a;
}

这样,当你调用new CPU这样的语句时候,编译器就主动报错去阻止你了。微笑


最后赞一下SUBLIME EDIT,类定义没有写分号时候,通过颜色给予了我强烈的谴责生气






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值