1. 为多态基类声明虚析构函数
工厂ShoesFactory根据鞋子类型SHOES_TYPE生产不同类型的鞋子NiKeShoes和AdidasShoes,这两种鞋子是以Shoes为基础的:
/* 鞋子基类 */
接下来开始生产
ShoesFactory
问题就出在释放动态内存时。C++创建对象时先创建基类成分,再创建派生成分(声明于派生类中、基类所没有的成员变量)。析构时则反过来,先释放派生成分,再释放基类成分。但这里只释放了父类部分,没有释放派生类的部分,造成对象局部销毁,从而导致内存泄漏。
解决办法是给基类声明virtual析构函数,这样每个派生类都将会有自己的析构函数。
class
插播一条知识点:基类的指针或者引用可以指向或者引用派生类的对象,派生类可以重写基类中的成员函数,可以在基类中将被重写的成员函数设置为虚函数。当通过基类的指针或者引用调用该成员函数时,将根据指针指向的对象类型确定调用的函数,而非指针的类型。
注:上述代码参考了
C++ 深入浅出工厂模式(初识篇)juejin.im
这是一篇讲工厂模式挺不错的的文章,推荐阅读。
2. 不要随便为基类声明虚析构函数
一个类不含虚函数,表明其很可能不作多态基类:
class
具有两个int变量的Point类可放入64位缓存器(int是32位),也可以作为一个64位的变量传递给其他语言(如C)写的函数。
由于实现了虚函数的类对象需要额外携带虚函数表(virtual table,vtbl) 和 虚函数表指针(virtual table pointer,vptr),用于在运行时决定哪个虚函数该被调用,类对象的体积会比不用虚函数的对象更大。因此,如果Point类的析构函数被声明为虚函数,其对象将无法放入64位缓存器,也无法和其他语言内的相同声明具有相同的结构 (其他语言的对应物没有vtbl)。
并非所有基类的设计目的都是为了多态用途,如条款6中为了避免类复制而设计的基类。经验是:当类内至少含一个virtual函数时,才为它声明virtual析构函数。
3. 为抽象类声明虚析构函数
使用纯虚函数可以设计不能实例化的抽象类,而抽象类通常是作为基类的。抽象类需要纯虚函数,基类需要虚析构函数,两相结合,声明和定义一个纯虚析构函数即可构成一个抽象类
class
由于编译器会在派生类的析构函数中创建一个对基类析构函数的调用,所以必须对基类的析构函数进行定义,否则连接器会发出警告。
4. 警惕对含非虚析构函数的类的继承
std::string并不用作基类,其析构函数不是虚函数,但是还是有人错误地将其作为基类:
class
就有可能发生像第一小节所说的内存泄漏:
SpecialString
注意所有STL容器均不用作基类。
5. 总结
- 作为程序调用者
- 如果遇到类含有虚函数和虚析构函数,那么这个类是被设计用于可继承的多态基类。
- 如果遇到类有纯虚函数,那么这个类是被设计为用于继承的抽象基类。
- 如果遇到类的函数都不是virtual,那么这个类可能是用于继承也可能是不用于继承的普通类。
- 作为程序设计者
- 如果一个类既不用于多态也不用于抽象,那就不要将析构函数声明为virtual。
- 如果打算设计一个多态类,那么必须将析构函数声明为virtual。
这里要区分以下几种类: