C++中的虚析构函数

以前没太注意基类的C++的析构函数是否为虚函数的问题,今天在拷贝一个教程的例子下来跑的时候报错了,后面发现该例子中的析构函数都没写,报了一个错,等下再说这个错误。
先看看基类析构函数为虚析构函数的应用场景:
一个基类指针指向子类,当删除这个基类指针时,在基类的析构函数为虚函数前提下,此时会自动调用子类的析构函数,释放子类所有内存的目的,防止内存泄漏。
这种基类指针指向子类的方式,在设计模式中非常常见。

我们通过例子来体会这段话:
例一:

#include <iostream>
using namespace std;

class Apple
{
public:
	Apple(){}
	~Apple()
	{
		std::cout << "delete Apple" << std::endl;
	}
};

// 基类
class Base {
public:
	Base() {};
	virtual ~Base() {  // 基类的析构函数是虚函数!
		cout << "delete Base\n";
	};
	void DoSomething() {
		cout << "Do Something in class Base!\n";
	};
};

// 派生类
class Derived : public Base {
public:
	Derived() {};
	~Derived() {
		cout << "delete Derived\n";
	};
	void DoSomething() {
		cout << "Do Something in Derived\n";
	};

private:
	Apple m_apple;
};

int main() 
{
	Base *b = new Derived();
	b->DoSomething();  // 重写DoSomething函数
	delete b;
	
	system("pause");
	return 0;
}

运行结果:

Do Something in class Base!
delete Derived
delete Apple
delete Base
请按任意键继续. . .

此时Derived中的析构函数被调用了,它所拥有的Apple也被释放了,一切看起来很好,没有内存泄漏。
但是如果Base的析构函数不是虚析构,而只是普通析构函数呢?
测试一下:
例二:(在例一的代码上修改,只将Base的析构函数前面的virtual去掉)

// 基类
class Base {
public:
	Base() {};
	~Base() {  // 基类的析构函数是虚函数!
		cout << "delete Base\n";
	};
	void DoSomething() {
		cout << "Do Something in class Base!\n";
	};
};

其余代码不变,运行结果:

Do Something in class Base!
delete Base
请按任意键继续. . .

Derived中的析构函数没有被调用,它拿着的Apple没有被释放,内存泄漏了。
其实虚析构函数也是虚函数,基类的指针指向子类时,调用虚函数的话,如果该虚函数在子类中有被重写(不管是自己重写的,还是系统自动添加的),那么子类的虚函数就会被调用。
我们还是通过例子来体会这句话:
例三:

#include <iostream>
using namespace std;

class Apple
{
public:
	Apple(){}
	~Apple()
	{
		std::cout << "delete Apple" << std::endl;
	}
};

// 基类
class Base {
public:
	Base() {};
	virtual ~Base() {  // 基类的析构函数是虚函数!
		cout << "delete Base\n";
	};
	void DoSomething() {
		cout << "Do Something in class Base!\n";
	};
};

// 派生类
class Derived : public Base {
public:
	Derived() {};
	//~Derived() {
	//	cout << "delete Derived\n";
	//};
	void DoSomething() {
		cout << "Do Something in Derived\n";
	};

private:
	Apple m_apple;
};

int main() 
{
	Base *b = new Derived();
	b->DoSomething();  // 重写DoSomething函数
	delete b;
	
	system("pause");
	return 0;
}

运行结果:

Do Something in class Base!
delete Apple
delete Base
请按任意键继续. . .

编译器自动帮我们添加了子类的虚构函数,而且是虚析构函数(为什么?因为基类的析构函数是virtual,子类的析构函数自然就是虚析构函数了)
这次内存也没泄漏,一切运行正常。
等等,还有一种情况呢,就是子类的指针指向子类,这时候就不用怀疑了,但是为了验证,我们还是测试一下。
例四:

#include <iostream>
using namespace std;

class Apple
{
public:
	Apple(){}
	~Apple()
	{
		std::cout << "delete Apple" << std::endl;
	}
};

// 基类
class Base {
public:
	Base() {};
	virtual ~Base() {  // 基类的析构函数是虚函数!
		cout << "delete Base\n";
	};
	void DoSomething() {
		cout << "Do Something in class Base!\n";
	};
};

// 派生类
class Derived : public Base {
public:
	Derived() {};
	~Derived() {
		cout << "delete Derived\n";
	};
	void DoSomething() {
		cout << "Do Something in Derived\n";
	};

private:
	Apple m_apple;
};

int main() 
{
	Derived *d = new Derived();
	d->DoSomething();  // 重写DoSomething函数
	delete d;
	
	system("pause");
	return 0;
}

运行结果:

Do Something in Derived
delete Derived
delete Apple
delete Base
请按任意键继续. . .

例五:(在例四基础上修改)

int main() 
{
	{
		Derived derived;
		derived.DoSomething();  // 重写DoSomething函数
	}
	system("pause");
	return 0;
}

运行结果:

Do Something in Derived
delete Derived
delete Apple
delete Base
请按任意键继续. . .

例四和例五就是我们常用的,子类调用子类的析构函数,子类析构过程是:先调用基类的析构函数,再执行子类析构函数。这时候也不会有内存泄漏。
小计一下:
1.在基类中析构函数建议加上virtual关键字,使之成为虚析构函数,这样在基类指针指向子类时,删除该基类指针才能调用子类析构函数,不造成内存泄漏。
2.当某个类没有子类时,析构函数当然可以不用虚析构函数了。

另外虚函数的写法是基类中加virtual,就像例一种的virtual ~Base();一样,子类中的析构函数不用加,这是虚函数的一种通常写法。
另外c++11中起,子类的重写基类的虚函数最好加上override,否则编译器会有警告。
就像下面这样:

class Base {
public:
	Base() {};
	virtual ~Base() {  // 基类的析构函数是虚函数!
		cout << "delete Base\n";
	};
	void DoSomething() {
		cout << "Do Something in class Base!\n";
	};
};

// 派生类
class Derived : public Base {
public:
	Derived() {};
	~Derived() override{
		cout << "delete Derived\n";
	};
	void DoSomething() {
		cout << "Do Something in Derived\n";
	};

private:
	Apple m_apple;
};

参考:
https://blog.csdn.net/cdlwhm1217096231/article/details/90604166
https://www.cnblogs.com/albizzia/p/8979078.html

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值