条款34:区分接口继承和实现继承
接口继承和实现继承,即pure virtual和impure virtual或者non-virtual成员函数,现在我们可以查看如下代码:
class Shape{
public:
virtual void draw() const=0;
virtual void error(const std::string& msg);
int objectID() const;
...
};
class Rectangle:public Shape{...};
class Ellipse:public Shape{...};
- 声明一个pure virtual函数的目的是为了让derived classes只继承函数接口。
针对订购ModelC飞机,而其的飞行方式和Airplane所提供的飞行方式并不一样,我们通过在Airplane中定义接口,然后在每个Model中进行实现即可,即定义一个pure virtual函数声明,然后在各个子类中进行实现。
class Airplane{
public:
virtual void fly(const Airport& destination)=0;
...
};
//提供pure virtual函数的缺省实现
void Airplane::fly(const Airport& destination){
...
}
class ModelA:public Airplane{
public:
virtual void fly(const Airport& destination)
{
Airplane::fly(destination);
}
...
};
class ModelB:public Airplane{
public:
virtual void fly(const Airport& destination)
{
Airplane::fly(destination);
}
...
};
class ModelC:public Airplane{
public:
virtual void fly(const Airport& destnation);
...
};
void ModelC::fly(const Airport& destination)
{
...//定义C型飞机的自己的飞行实现
}
PS:pure virtual函数可以被定义,没想到吧!!!参看下代码:
#include <iostream>
#include <string>
using namespace std;
class Base{
public:
Base(int x) :x(x){
}
virtual void mf0() = 0{
cout << "hello,我是pure virtual函数,却可以被实现,Base::mf0(),没想到吧!!!" << endl;
};
private:
int x;
};
class Derived :public Base{
public:
Derived(int x) :Base(x){
}
virtual void mf0(){
cout << "hello,I'm the Derived's mf0()函数" << endl;
}
};
int main(){
Derived d(10);
d.Base::mf0();
d.mf0();
return 0;
}
运行结果:
- 声明impure virtual函数的目的是为了让derived classes继承该函数的接口和缺省实现。
class Airport{...};
class Airplane{
public:
virtual void fly(const Airport& destination);
...
};
void Airplane::fly(const Airport& destination){
...
}
class ModelA:public Airplane{...};
class ModelB:publc Airplane{...};
但是此时公司又订了一批新的飞机ModelC,它的飞行方法和之前的飞行方法不一样,然而此时我们忘记为ModelC定义相应的fly方法了,因为有默认的实现呀!!!这样会导致ModelC出现极大灾难!
class ModelC:public Airplane{
...
};
Airport PDX(...);
Airplane* pa=new ModelC;
...
pa->fly(PDX);
为了避免这种问题,我们需要切断“impure virtual函数接口”和“默认实现”之间的连接,参看如下代码:
class Airplane{
public:
virtual void fly(const Airport& destination)=0;
...
protected:
void defaultFly(const Airport& destination);
};
void Airplane::defaultFly(const Airport& destination){
...
}
class ModelA:public Airplane{
public:
virtual void fly(const Airport& destination){
defaultFly(destnation);
}
...
};
class ModelB:public Airplane{
public:
virtual void fly(const Airport& destination)
{
defaultFly(destination);
}
...
};
class ModelC:public Airplane{
public:
virtual void fly(const Airport& destination){
defaultfly(destination);
}
...
};
void ModelC::defaultFly(const Airport& destination)
{
...//定义ModelC的飞行设计
}
这样的设计显然比以前的设计显得更可靠一些,至于Airplane::defaultFly,请注意其现在变成了protected,因为它是Airplane及其derived classes的实现细目,乘客应该只在意飞机怎么飞,不管它们怎么实现的。
- 声明non-virtual函数的目的是为了令derived classes继承函数的接口及一份强制性实现。
1、策略:pure virtual函数、impure virtual函数、non-virtual函数之间的差异,使你得以精确指定你想要derived classes继承的东西:只继承接口,或者继承接口和一份缺省实现,亦或是继承接口和一份强制实现,因此当你声明你的base classes的成员函数时候,需要谨慎选择,认真思考过后然后为base classes的成员函数进行属性配置。
2、常见错误:
- 将所有函数声明为non-virtual,这样使得derived classes没有余域空间进行特化工作,non-virtual的析构函数尤其会带来问题。
- 将所有成员函数声明为virtual,有时候这样做是正确的,然而这也可能是class设计者缺乏鉴定立场的前兆,某些函数就是不该在derived class中被重新定义,果真如此你应该将那些函数声明为non-virtual,没有人有权利妄称你的class适用于任何人任何事任何物他们只需花点时间重新定义你的函数就可以享受一切。
总结:
1)接口继承和实现继承不同,在public继承之下,derived classes总是继承base class的接口;
2)pure virtual函数只具体指定接口继承;
3)impure virtual函数具体指定接口继承及缺省实现继承。