作为类的设计者,有时希望派生类只继承成员函数的接口(声明);有时希望派生类同时继承函数的接口和实现,但允许派生类改写实现;有时则希望同时继承接口和实现,并且不允许派生类改写任何东西。

class ASCEShape

{

public:

         virtual void draw() const = 0;     //纯虚函数,因此ASCEShape成为一个抽象类

         virtual void error(const string& msg);         //一般虚函数

         int objectID() const;      //非虚函数

         ...

};

class ASCERectangle : public ASCEShape {...};

class ASCEEllipse : public ASCEShape {...};


纯虚函数最显著的特征是:它们必须在继承了它们的任何具体类中重新声明,而且它们在抽象类中往往没有定义。因此:定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口

为一个纯虚函数提供定义也是可能的,我们可以为ASCEShape::draw提供实现,C++编译器不会报错,但调用它的唯一方式是通过类名完整的指明是哪个调用:

//假设在ASCEShape中定义了纯虚函数draw

ASCEShape *ps = new ASCEShape;     //错误,ASCEShape是抽象类


ASCEShape *ps1 = new ASCERectangle;    //正确

ps1->draw();          //调用ASCERectangle::draw


ASCEShape *ps2 = new ASCEEllipse; //正确

ps2->draw();                   //调用ASCEEllipse::draw


ps1->ASCEShape::draw();    //调用ASCEShape::draw

ps2->ASCEShape::draw();   //调用ASCEShape::draw

(纯虚函数必须在子类中重新声明,但它还是可以在基类中有自己的实现)


有时,声明一个除了纯虚函数之外什么也不包含的类是很有用的,这样的类叫做协议类。它为派生类仅提供函数接口,完全没有实现。

声明一般虚函数的目的在于:使派生类继承函数的接口和缺省实现。


声明非虚函数的目的在于:使派生类继承函数的接口和强制性实现。注意,非虚函数表示一种特殊性上的不变性,所以决不能在子类中重新定义。


理解了纯虚函数。简单虚函数和非虚函数在声明上的区别,我们就可以精确地指定想让派生类继承什么:仅仅是接口,还是接口和一个缺省实现,或者是接口和一个强制实现。


常见的错误:

1)把所有的函数都声明为非虚函数。这就使得派生类没有特殊化的余地。事实上是:几乎任何一个作为基类使用的类都有虚函数。

2)将所有的函数都声明为虚函数。有时这没错,例如协议类。但往往一些函数不能在派生类中重定义,只要是这种情况,我们就应该将它声明为非虚函数。