条款34:区分接口继承和实现继承

来源:互联网 发布:杭州美工设计培训班 编辑:程序博客网 时间:2024/05/29 02:32

(一)

class Shape { public:     virtual void draw() const = 0;     virtual void error(const string& msg);     int objectID() const; }; class Rectangle : public Shape {...}; class Ellipse : public Shape {...};
公有继承的概念包含两个独立的部分函数接口的继承函数实现的继承。两者之间的差别恰与函数实现之间相异之处等价。成员函数的接口总是被继承,因为public继承意味is-a。
纯虚函数有两个突出特征:(1)必须被任何“继承它们”的具象class重新声明;

 (2)它们在抽象class中通常没有定义。

(二)声明一个pure virtual纯虚函数的目的

声明一个纯虚函数的目的是为了让derived class只继承函数接口。我可以为它提供一份实现代码,但调用它的唯一途径是“调用时明确指出其class名称”。

Sharp* ps1 = new Rectangle;ps1->draw();//调用Rectangle::drawSharp* ps2 = new Ellipse;ps2->draw(); //调用Ellipse::drawps1->Shape::draw();//调用Shape::drawps2->Shape::draw();//调用Shape::draw


(三)声明一个impure virtual 函数的目的

声明一个impure virtual 函数的目的是让Derived class 继承该函数的接口缺省实现

允许impure virtual函数同时指定函数声明和函数缺省行为,有可能造成危险。如下面飞机的例子

class Airport {...}; class Airplane { public:     virtual void fly(const Airport& destination);     ...};void Airplane::fly(const Airport& destination) {     //缺省代码,将飞机飞至指定目的地 } class ModelA : public Airplane {...};//A和B飞机飞往目的地相同class ModelB : public Airplane {...};//A和B飞机飞往目的地相同class ModelC : public Airplane {     //C与A,B飞行方式不同...//未声明fly函数,但它并不希望缺省飞行};Airport PDX(...); Airplane* pa = new ModelC; ... pa->fly(PDX);  //未说出“我要的情况下就继承了该缺省行为,酿成大灾难”
这个程序试图以ModelA或者ModelB的飞行方式来飞ModelC。

问题不在于Airplane::fly有缺省行为,而在于ModelC在没有明白的说明“我要”的情况下就继承了改缺省行为。幸运的是,我们可以轻而易举的做到“提供缺省实现给derived class,但除非它们明白要求否则免谈”。

解决方法1:此间伎俩在于切断“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(destination);     }     ... }; class ModelB: public Airplane { public:     virtual void fly(const Airport& destination) {         defaultFly(destination);     }     ... };
fly被改成为一个pure virtual函数,只提供飞行接口。缺省行为以defaultFly出现在Airplane class中。若想使用缺省实现,在fly中对default做一个inline调用。

现在,ModelC不可能意外继承不正确的fly实现代码了。因为Airplane中的 fly是pure virtual函数。迫使ModelC必须提供自己的fly版本:

class ModelC: public Airplane { public:     virtual void fly(const Airport& destination);     ... }; void ModelC::fly(const Airport& destination) {     //将C型飞机飞至指定的目的地 }

像这样,也并非安全无虞,还可能因为剪贴(copy-and-paste)代码而招来麻烦,但是它比原来的设计值得依赖。

有些人反对以不同的函数分别提供接口和缺省实现,想上面说所的fly和defaultFly那样。

可以用下面的解决方法:

解决方法2:

可以用“pure virtual函数必须在derived中重新声明,但是它们可以拥有自己的实现”这一事实。下面是AirPlane继承体系如何给pure virtual函数的一份定义。

class Airplane { public:     virtual void fly(const Airport& destination) = 0;     ... }; void Airplane::fly(const Airport& destination) // pure virtual 函数实现{     //缺省行为,将飞机飞至指定目的地 } 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& destination)     ... }; void ModelC::fly(const Airport& destination) {     // 将C型飞机飞至指定目的地 }

(四)

如果成员函数是个non-virtual函数,意味着它并不打算在derived classes中游不同的行为。non-virtual成员函数所表现的不变性凌驾其特异性,无论derived class变的多么特异化,它的行为都不可以改变。

声明non-virtual函数的目的是为了derived class 继承函数的接口以及一份强制性实现。


class Shape { public:     ...    int objectID() const; }; 
Shape::objectID的声明:可以想做是“每个Shape对象都有一个用来产生对象识别码的函数:此识别码总是采用相同的计算方法,该方法由Shape::objectID的定义式决定,任何derived class都不应该尝试改变其行为”。


请记住:

1 接口继承和实现继承不公,在public继承之下,derived classes总是继承 base class的接口。

2pure virtual 函数只具体指定接口继承。

3 impure virtual 函数具体指定接口继承及缺省实现继承。

4non-virtual函数具体指定接口以及强制性实现接口。


0 0
原创粉丝点击