条款34:区分接口继承以及实现继承
在普通的public继承中,实际上也分为接口以及实现继承。首先看下面这个例子:
1 class Shape{ 2 public: 3 virtual void draw() const = 0; 4 virtual void error(const std::string & msg); 5 int ObjectId() const; 6 }; 7 class Recantagle : public Shape{ 8 ... 9 }; 10 class Ellipse : public Shape{ 11 ... 12 };
首先,pure virtual函数必须被继承了他们的具体class重新声明,这是典型的接口的性质,(例如java中的接口)。所以说:声明一个pure virtual函数只是为了让derived class只继承其接口。而且甚至还可以给virtual函数一个定义,只要不声明这个对象就可以了。使用的方法是加上基类的限定符,即Shape::draw();
再者,声明impure virtual函数的目的,是让derived class接受该函数的接口以及默认的实现。但是有时候可能会忘了实现Derived版本的相应函数,导致继承了基类版本的不希望用的实现。例如下面这个例子:
1 class Ariport{ 2 ... 3 }; 4 class Airplane{ 5 public: 6 virtual void fly(const Ariport & dest); 7 ... 8 }; 9 class ModelA : public Airplane{ 10 ... 11 };
这里可能会忘了做ModelA的相应的fly实现从而使用了可能不希望使用的基类版本,这可以通过另一种方式来解决这种问题。
1 class Airplane{ 2 public: 3 virtual void fly(const Airplane & dest) = 0; 4 ... 5 protected: 6 void defaultFly(const Airplanes & dest); 7 }; 8 class ModelA : public Airplane{ 9 public: 10 virtual void fly(const Airplane & dest) 11 {defaultFly(dest);} 12 ... 13 }; 14 class ModelB : public Airplane{ 15 public: 16 virtual void fly(const Airplane & dest) 17 {//单独为自己机型定义的fly函数} 18 ... 19 };
这样就不会忘了为特定的机型定义特定的fly函数了。上面的方法实际上通过不同的函数分别提供了接口以及缺省实现。
当然,大众的实现上面所说的方法还是实现一份纯虚函数,并且给他一份定义即可 :
1 class Airplane{ 2 public: 3 virtual void fly(const Airplane & dest) = 0 4 { 5 ....//自定义的实现 6 } 7 ... 8 }; 9 class ModelA : public Airplane{ 10 public: 11 virtual void fly(const Airplane & dest) 12 {Airplane::fly(dest);} 13 ... 14 }; 15 class ModelB : public Airplane{ 16 public: 17 virtual void fly(const Airplane & dest) 18 {//自定义的fly行为} 19 ... 20 };
这里即将用同一个函数提供了接口以及非默认的实现。这样做有一个小小的缺点就是失去可定义的一个protected函数(这是一个实现)。
而非虚函数的意义就是:使得derived拥有基类声明的一个强制性的实现。
pure virtual, virtual, 以及 none pure函数之间的差异提供两个给了我们几个选择:只继承接口,继承接口以及默认的实现,以及只集成默认实现。
小结:
接口继承与实现继承之间是不同的,public继承下,derived class总是集成base class的接口
pure virtual函数只是具体指定接口集成
impure virtual函数具体指定接口继承以及缺省的实现继承。
non-virtual函数具体指定了接口集成以及强制性的实现继承