理解虚析构函数

Posted Redamanc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解虚析构函数相关的知识,希望对你有一定的参考价值。

示例代码(普通析构)

老样子,我们先来看如下代码:

class Base
{
public:
	Base(int data) :ma(data) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
	void show() { cout << "call Base::show()" << endl; }
private:
	int ma;
};

class Derive :public Base
{
public:
	Derive(int data) 
		:Base(data), mb(data)
	{
		cout << "Derive()" << endl;
	}
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;
};

int main()
{
	Base* pb = new Derive(10);
	pb->show(); 
	delete pb; 
	
	return 0;
}

存在问题

我们先来看运行结果:
在这里插入图片描述
发现问题了吗?
缺少了~Derive()!也就是说,派生类的析构函数根本没有被执行!
这样一来,就会存在内存泄漏的问题。

原因分析

那么为什么不会调用派生类的析构函数呢?
原因就在于我们是用基类的指针,指向了上new出来的派生类对象
并且,基类的析构函数是普通函数,在函数调用的过程中,发生了静态绑定
也就是说,在编译时期,编译器就确定了调用析构函数的地址只有基类的析构函数;
派生类的析构函数没有机会得到执行。

修改代码(虚析构)

分析完原因之后,我们知道,调用析构函数的时候,必须发生动态绑定,否则会导致派生类的析构函数无法调用,故,修改代码如下:

class Base
{
public:
	Base(int data) :ma(data) { cout << "Base()" << endl; }
	virtual ~Base() { cout << "~Base()" << endl; }
	virtual void show() { cout << "call Base::show()" << endl; }
private:
	int ma;
};

class Derive :public Base
{
public:
	Derive(int data) 
		:Base(data), mb(data)
	{
		cout << "Derive()" << endl;
	}
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;
};

int main()
{
	Base* pb = new Derive(10);
	pb->show(); 
	delete pb; 
	
	return 0;
}

这个时候我们再次运行程序:
在这里插入图片描述
这样一来,就能正确的析构派生类对象了。

相关问题

当然,关于虚析构函数还有很多相关的问题:

构造函数可以是虚函数吗?

要回答这个问题,我们需要了解虚函数依赖什么?
首先,虚函数能够产生地址,存储在vftable中,
其次,虚函数必须依赖对象,换句话说,对象必须存在
因为只有对象存在,通过对象的虚函数指针vfptr,访问到只读数据区vftable,这样才能得到虚函数的地址。
分析到这里,我们可以得出结论:构造函数不可以是虚函数
因为构造函数调用的时候,还没有对象

什么时候需要虚析构函数?

当基类的指针(引用)指向堆上new出来的派生类对象的时候
delete pb(基类的指针)调用析构函数的时候,必须发生动态绑定;
否则会导致派生类的析构函数无法调用。

以上是关于理解虚析构函数的主要内容,如果未能解决你的问题,请参考以下文章

为啥纯虚析构函数需要实现

析构函数的虚析构和非虚析构调用的差别

C++ 类的多态四(虚析构函数的重要性)

局部抽象类的纯虚析构函数

虚析构函数详解

C++ --- 虚析构和纯虚析构