条款07:为多态基类声明virtual析构函数
1、为什么要给多态设计带多态性质的基类声明virtual析构函数?
考虑一个场景:有一个基类指针指向了一个派生类对象,而这个派生类对象创建在堆内存上。当通过delete关键字回收这块内存时,delete的是基类的指针。此时,就会发生一种怪异的现象:该派生类的基类部分被回收了,但是派生类部分没有被回收。即:导致了内存的泄露。实际上,此时只执行了基类的析构函数,没有执行派生类的析构函数。因此,即使该对象没有在堆内存上创建,但是如果析构函数中函数某些逻辑代码时,也为导致不明确行为的发生。这时虽然没有发生内存泄露,但是代码却出错了。因此,多态基类声明virtual析构函数。
2、为什么不作为多态用途使用的类的析构函数一定不能加virtual?
有下面两个原因:
- 浪费内存
- 降低了移植性
将析构函数声明为virtual,则代表这个类作为多态使用。而作为多态使用的类是有一定内存消耗的:多态类的实例化对象内部都有一个指针,指向虚函数表。而每一个作为多态使用的类都有一个虚函数表,用来在运行程序时确定执行哪一个函数。当将一个不作为多态使用的类的析构函数声明为virtual时,首先会增加这些内存。其次,从语言移植性来讲,由于破坏了类对象的原本的存储的结构,C++代码向C或其它代码的一致性也降低了。
3、欲将一个类设计为抽象类,却没有合适可用的纯虚函数时的一个技巧
首先抽象类一定是作为基类使用的,而抽象类一定会作为多态使用。因此可以将其析构函数声明为纯虚函数。但是这里有一个注意的点,即:需要为该纯虚函数提供一份定义。
class AWOV{
virtual ~AWOV()=0;
}
AWOV::~AWOV(){}
4、避免继承标准库中不作为多态使用的类
标准库中不作为多态使用的类(带有non-virtual的类):
所有容器类(vector、set等等)、string这些类设计出来不作为基类使用,更不是作为多态使用。某些类设计出来作为基类使用,但是不作为多态使用。
使用有上述这些类派生出来的类可能导致意想不到的情况。