C++纯虚函数和抽象类详解

纯虚函数就是没有函数体的虚函数。包含纯虚函数的类就叫抽象类。下面的类 A 就是一个抽象类:
class A {
private:
    int a;
public:
    virtual void Print() = 0;  //纯虚函数
    void fun1() { cout << "fun1"; }
};
Print 就是纯虚函数。纯虚函数的写法就是在函数声明后面加=0,不写函数体。纯虚函数实际上是不存在的,引入纯虚函数是为了便于实现多态。

之所以把包含纯虚函数的类称为“抽象类”,是因为这样的类不能生成独立的对象。例如定义上面的 class A 后,下面的语句编译都会出错:
A a;
A* p = new A;
A a[2];
既然抽象类不能用来生成独立对象,那么抽象类有什么用呢?抽象类可以作为基类,用来派生新类。可以定义抽象类的指针或引用,并让它们指向或引用抽象类的派生类的对象,这就为多态的实现创造了条件。独立的抽象类的对象不存在,但是被包含在派生类对象中的抽象类的对象是可以存在的。

抽象类的概念很符合逻辑。在《C++多态的好处和作用》一节中,我们设计了一款“魔法门”游戏,其中 CCreature 类的写法如下:
class CCreature {  //“怪物”类
protected:
    int lifeValue, power;
public:
    virtual void Attack(CCreature* p) {}
    virtual void Hurted(int nPower) {}
    virtual void FightBack(CCreature* p) {}
};
在该程序中,实际上不需要独立的 CCreature 对象,因为一个怪物对象要么代表“狼”,要么代表“龙”,要么代表“雷鸟”,总之是代表一种具体的怪物,而不会只是一个抽象的、什么都不是的“怪物”类的对象。所以,上面的 CCreature 类中的 Attack、Hurted、FightBack 成员函数也都没有实际操作。

既然如此,将上面三个成员函数声明为纯虚函数,从而把 CCreature 类变成一个抽象类,就是很恰当的了。因此,CCreature 类的改进写法如下:
class CCreature {  //“怪物”类
protected:
    int lifeValue, power;
public:
    virtual void Attack(CCreature* p) = 0;
    virtual void Hurted(int nPower) = 0;
    virtual void FightBack(CCreature* p) = 0;
};
同理,对于《C++多态的好处和作用》一节中的几何形体程序,几何形体对象要么是圆形,要么是三角形,要么是矩形,等等,也不存在抽象的 CShape 类的对象,因此 CShape 类应该如下改写为抽象类:
class CShape  //基类:形体类
{
public:
    virtual double Area() = 0;  //求面积
    virtual void Printlnfo() = 0;  //显示信息
};
如果一个类从抽象类派生而来,那么当且仅当它对基类中的所有纯虚函数都进行覆盖并都写出函数体(空的函数体{}也可以),它才能成为非抽象类。

思考题:在抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内部不能调用纯虚函数。为什么?