条款07这里需要补充的知识点(我觉得很重要):
一.如果一个类不企图被当作基类,那么令其析构函数为虚函数是很愚蠢的。正确的做法应该是:只有当类中有至少一个虚函数时,才把其析构函数声明为虚析构函数。
因为虚函数的作用是为了动态联编(实现多态),而为了实现动态联编,对象需要额外多出vptr指针,会增加体积(sizeof可以明显看出),vptr指向一个由函数指针构成的数组,称为vtbl(虚表),每一个带有虚函数的类都有一个相应的vtbl,实际被调用的函数取决于该对象的vptr所指的那个vtbl。(vptr的分步初始化知识点见笔记)
二.有时我们希望拥有抽象类,但手上又没有任何纯虚函数,怎么办?
1.由于抽象类总是被企图当作一个基类,又由于多态基类为了防止内存泄漏,应该有个虚析构函数,因此,解决办法就是为这个抽象类声明一个纯虚析构函数。
2.但又因为派生类对象析构时需要调用抽象类(基类)的析构函数(回顾一下析构函数的运作方式:最远的派生类的析构函数最先被调用,然后是各个基类的析构函数被调用,和构造函数的调用顺序恰好相反),而抽象类的纯虚构函数是没有函数体的,这样就会报错。所以解决办法是在抽象类的类外实现其函数体,而不是在派生类中重写,这是纯虚析构函数和其它纯虚函数相比的特殊之处。(详见下文“纯虚函数也可以有函数体”);
例子如下:
#include<iostream>
using namespace std;
class Base {//抽象类
public:
virtual ~Base() = 0;//纯虚析构函数
};
Base::~Base() { cout << "Base destructor" << endl; }//纯虚函数也可以有函数体
class Derive :public Base {
public:
~Derive() { cout << "Derive destructor" << endl; }//派生类的析构函数,是虚函数(省略virtual)!
};
int main() {
Base *pb = new Derive();//在堆内存上new一个派生类的对象,然后使基类指针指向派生类的对象
delete pb;
system("pause");
return 0;
}