为什么是虚析构函数?

CPP玩多了你自然就会接触很多关于继承的实例,在继承体系中我们经常会为了多态特性去使用很多的virtual关键字。其中会有对普通成员函数的virtual修饰,对继承关系的virtual修饰,见得更多的还有对父类析构函数的virtual修饰,那么为什么父类的析构函数是虚的那?如果不用virtual修饰父类的析构函数会怎么样?

先看如下两个实例:

class B
{
public:
	B(){ std::cout << "construct B !" << std::endl; }
	virtual ~B(){ std::cout << "destruct B ~" << std::endl; }

	virtual void Print(){std::cout << "Print for B @" << std::endl;}
};

class D : public B
{
public:
	D(){ std::cout << "construct D !" << std::endl; }
	~D(){ std::cout << "destruct D ~" << std::endl; }

	virtual void Print(){std::cout << "Print for D @" << std::endl;}
};

int main()
{
	B *b = new D;
	delete b;

	return 0;
}
运行结果:

construct B !
construct D !
destruct D ~
destruct B ~


将基类里面的析构函数virtual去掉后

<span style="font-size:14px;">class B
{
public:
	B(){ std::cout << "construct B !" << std::endl; }
	~B(){ std::cout << "destruct B ~" << std::endl; }

	virtual void Print(){std::cout << "Print for B @" << std::endl;}
};

class D : public B
{
public:
	D(){ std::cout << "construct D !" << std::endl; }
	~D(){ std::cout << "destruct D ~" << std::endl; }

	virtual void Print(){std::cout << "Print for D @" << std::endl;}
};

int main()
{
	B *b = new D;
	delete b;

	return 0;
}</span>


运行结果如下所示:

construct B !
construct D !
destruct B ~


           显而易见,当我们在有多态的继承情况下,如果失去了对父类析构函数的Virtual修饰,那么在释放对象时,子类的析构函数将无法得到调用。众所周知,类对象的资源释放都是在析构函数中得以实现,比如堆内存的free,成员对象的析构等等。如果析构函数没有被调用而漏掉了,最终将会导致的情况很明显是子类对象的类所持有的资源泄漏! 所以将Base的析构函数声明为虚函数,作用很明显是用Base类的指针删除一个派生类对象时,派生类对象的析构函数被调用。带多态性质的基类必须声明一个virtual析构函数。

         那么问题来了:我们上面时候才需要将一个父类的析构函数声明为虚析构函数?

         对与虚析构函数,并不是所有的class类都需要。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。Class的设计目的如果不是作为Base class使用,或不是为了具备多态性,就不应该声明virtual析构函数。那么为什么不是所有的class都加上virtual析构函数以保万一呢?事实上,如果没有必要,请不要为析构函数加上virtual特性。因为virtual是有代价的,为了实现virtual函数,类中间必须要增加一个虚指针Vptr指向虚函数表,这将增大类的体积。常规的判定规则是只有当类当中有virtual的成员函数时,说明这个基类是用作多态,析构函数才声明为virtual。

       PS: C++对象模型中介绍到,虚函数的实现需要它所在的对象包含额外的信息,这一信息主要用来在运行时动态的确定该对象的所属以及需要调用哪个虚函数。在实现中常常是让对象包含一个虚指针,这个指针被称为vptr 。 vptr 指向一个包含函数指针的数组,这一数组称为 vtbl(虚函数表)。这个表中的第一项slot是该对象的类型信息,其余每个solt都是函数指针。每个包含虚函数的都有一个与之相关的 vtbl 。当一个虚函数被一个对象调用时,就用到了该对象的 vptr 所指向的 vtbl ,在 vtbl 中查找一个合适的函数指针,然后调用相应的具体函数。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值