(一个)

  1. class Shape {
  2. public:
  3. virtual void draw() const = 0;
  4. virtual void error(const string& msg);
  5. int objectID() const;
  6. };
  7. class Rectangle : public Shape {...};
  8. class Ellipse : public Shape {...};

公有继承的概念看似简单。似乎非常轻易就浮出水面。然而细致审度之后,我们会发现公有继承的概念实际上包括两个相互独立的部分:函数接口的继承和函数实现的继承。二者之间的区别恰与函数声明和函数实现之间相异之处等价。成员函数的接口总是被继承,由于public继承意味is-a。

pure virtual 函数两个突出特征:(1)必须被不论什么“继承了它们”的具象class又一次声明。(2)它们在抽象class中通常未定义。

(二)

声明一个pure
virtual函数的目的是为了让derived class仅仅继承函数接口。我能够为它提供一份实现代码,但调用它的唯一途径是“调用时明白指出期class的名称”。

  1. Sharp* ps1 = new Rectangle;
  2. ps1->draw();
  3. Sharp* ps2 = new Rectangle;
  4. ps2->draw();
  5. ps1->Shape::draw();//调用Shape的draw
  6. ps2->Shape::draw();

(三)

声明impure
virtual函数的目的。是让derived classes继承该函数的接口和缺省实现。

同意impure
virtual函数同一时候指定函数声明和函数缺省行为,却有可能造成危急。

  1. class Airport {...};
  2. class Airplane {
  3. public:
  4. virtual void fly(const Airport& destination);
  5. };
  6. void Airplane::fly(const Airport& destination) {
  7. //缺省代码,将飞机飞至指定目的地
  8. }
  9. class ModelA : public Airplane {...};
  10. class ModelB : public Airplane {...};
  11. class ModelC : public Airplane {
  12. ...//未声明fly函数,但它并不希望缺省飞行
  13. };
  14. Airport PDX(...);
  15. Airplane* pa = new ModelC;
  16. ...
  17. pa->fly(PDX); //未说出“我要的情况下就继承了该缺省行为,酿成大灾难”

这个程序试图用ModelA和ModelB的飞行方式来飞ModelC。

问题不在Airplane::fly()有缺省行为,而在于ModelC在未明白说出“我要”的情况下就继承了该缺省行为。我们能够做到“提供缺省实现给derived classes。但除非它们明白要求,否则免谈”。

解决的方法(1):此间伎俩在于切断“virtual
函数接口”和其“缺省实现”之间的连接。以下是一种做法:

  1. class Airplane {
  2. public:
  3. virtual void fly(const Airport& destination) = 0;
  4. ...
  5. protected:
  6. void defaultFly(const Airport& destination);
  7. };
  8. void Airplane::defaultFly(const Airport& destination) {
  9. //缺省行为,将飞机飞至目的地
  10. }

fly已被改成为一个pure
virtual函数,仅仅提供飞行接口。缺省行为以defaultFly出如今Airplane class中。

若想使用缺省实现(比如ModelA和ModelB)。能够在fly中对defaultFly做一个inline调用:

  1. class ModelA: public Airplane {
  2. public:
  3. virtual void fly(const Airport& destination) {
  4. defaultFly(destination);
  5. }
  6. ...
  7. };
  8. class ModelB: public Airplane {
  9. public:
  10. virtual void fly(const Airport& destination) {
  11. defaultFly(destination);
  12. }
  13. ...
  14. };

如今ModelC不可能意外继承不对的fly实现代码了。由于Airplane中的pure
virtual函数迫使ModelC必须提供自己的fly版本号:

  1. class ModelC: public Airplane {
  2. public:
  3. virtual void fly(const Airport& destination);
  4. ...
  5. };
  6. void ModelC::fly(const Airport& destination) {
  7. //将C型飞机飞至指定的目的地
  8. }

像这种话,这个方法并不是安全无虞。程序猿还是可能由于剪贴(copy-and-paste)代码而招来麻烦,但它比原来的设计值得依赖。

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

所以我们想出了以下这样的解决的方法:

解决的方法(2):

我们能够利用“pure
virtual函数必须在derived class中又一次声明。但它们能够拥有自己的实现”这一事实。

以下是Airplane继承体系怎样给pure virtual函数一份定义:

  1. class Airplane {
  2. public:
  3. virtual void fly(const Airport& destination) = 0;
  4. ...
  5. };
  6. void Airplane::fly(const Airport& destination) // pure virtual 函数实现 {
  7. //缺省行为,将飞机飞至指定目的地
  8. }
  9. class ModelA: public Airplane {
  10. public:
  11. virtual void fly(const Airport& destination) {
  12. Airplane::fly(destination);
  13. }
  14. };
  15. class ModelB: public Airplane {
  16. public:
  17. virtual void fly(const Airport& destination) {
  18. Airplane::fly(destination);
  19. }
  20. };
  21. class ModelC: public Airplane {
  22. public:
  23. virtual void fly(const Airport& destination)
  24. ...
  25. };
  26. void ModelC::fly(const Airport& destination) {
  27. // 将C型飞机飞至指定目的地
  28. }

(四)

假设成员函数是个non-virtual函数,意味着它并不打算在derived classes中有不同的行为。non-virtual 成员函数所表现的不变性凌驾其特异性,不管derived
class变得多么特异化。它的行为都不能够改变。

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

  1. class Shape {
  2. public:
  3. ...
  4. int objectID() const;
  5. };

来看Shape::objectID的声明:能够想做是“每一个Shape对象都有一个用来产生对象识别码的函数:此识别码总是採用同样计算方法,该方法由Shape::objectID的定义式决定,不论什么derived
class都不应该尝试改变其行为”。

请记住:

1.  接口继承&实现继承不同。在public继承之下。derived classes总是继承base
class的接口。

2.  Pure virtual函数仅仅详细指定接口继承。

3.  Impure virtual函数详细指定接口继承及缺省实现继承。

4.  non-virtual函数详细指定接口继承以及强制性实现继承。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

Effective C++:规定34:区分接口继承和实现继承的更多相关文章

  1. Effective C++ Item 34 区分接口继承与实现继承

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 关联条款 Item 36 接口继承和实现继承不同.在 public 继承下, derived ...

  2. 读书笔记 effective c++ Item 34 区分接口继承和实现继承

    看上去最为简单的(public)继承的概念由两个单独部分组成:函数接口的继承和函数模板继承.这两种继承之间的区别同本书介绍部分讨论的函数声明和函数定义之间的区别完全对应. 1. 类函数的三种实现 作为 ...

  3. Effective C++ 34 区分接口继承和实现继承

    public继承从根本上讲,有两部分:接口继承和实现继承.两者之前的区别很像函数声明与函数定义. 具体设计中,会呈现三种形式:derived class只继承成员函数的接口(纯虚函数):derived ...

  4. Effective C++ -----条款34:区分接口继承和实现继承

    接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口. pure virtual函数只具体指定接口继承. 简朴的(非纯)impure vir ...

  5. 条款34:区分接口继承和实现继承(Different between inheritance of interface and inheritance of implemenation)

    NOTE: 1.接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口. 2.pure virtual 函数只具体指定接口继承及缺省实现继承. ...

  6. [EffectiveC++]item34:区分接口继承和实现继承

    [EffectiveC++]item34:区分接口继承和实现继承

  7. 【C++】Item34.区分接口继承和实现继承

    区分接口继承和实现继承 类包含的成员函数种类 1.静态函数 2.非静态函数 2.1 普通函数(非虚) non-virtual 2.2 虚函数 2.2.1 纯虚函数 pure-virtual 2.2.2 ...

  8. 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承

    这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...

  9. C++接口继承与实现继承的区别和选择

    1.接口继承与实现继承的区别 <Effective C++>条款三十四:区分接口继承和实现继承中介绍的比较啰嗦,概括地说需要理解三点: (1)纯虚函数只提供接口继承,但可以被实现: (2) ...

随机推荐

  1. 开源 自由 java CMS - FreeCMS2.0 举APP产生信息数据

    项目地址:http://www.freeteam.cn/ 生成信息数据 生成当前管理网站下同意移动APP訪问的栏目的信息页面. 从左側管理菜单点击生成信息数据进入. 您能够选择须要生成的栏目,然后点击 ...

  2. unix pwd使用命令

    [语法]:     pwd [说明]:    此命令会显示当前的工作文件夹 []: pwd     这显示当前工作文件夹 版权声明:本文博主原创文章.博客,未经同意不得转载.

  3. 【iOS】Swift扩展extension和协议protocol

    加上几个关节前Playground摘要码进入github在,凝视写了非常多,主要是为了方便自己的未来可以Fanfankan. Swift语法的主要部分几乎相同的. 当然也有通用的.运算符重载.ARC. ...

  4. cocos2dx tolua传递参数分析

    cocos2dx tolua传递参数分析: tolua_Cocos2d_CCNode_addChild00 == void CCNode::addChild(CCNode *child) tolua_ ...

  5. 【2014 Multi-University Training Contest 2 1002】/【HDU 4873】 ZCC Loves Intersection

    果然,或滥用零件,啥都不说了.我们欣慰地学习阅读.这两天残疾儿童是数学. 这是求所需的问题,不明确.贴上官方的解题报告. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi ...

  6. Entity Framework笔记(二)

    前几日学习了在VS2010Console项目中使用Entity Framework,并且使用Code First模式.通过编写Model类,来生成数据库对应的表.并且,往表中写入数据以及获取表中的所有 ...

  7. iOS coreData

    static int row=0; static const NSString *kStoryboardName = @"LRCoreDataViewController"; st ...

  8. Android 墙纸设置代码 详细说明

    使游戏图像列表.思考添加壁纸功能.我发了一些资料. 1 别忘记在ApplicationManifest.xml 中加上权限的设置. <uses-permission android:name = ...

  9. hdu1023

    import java.math.BigInteger; import java.util.Scanner; public class Main { static BigInteger fac(Big ...

  10. [Visual Studio]透过Visual Studio 2012的选择性贴上将XML与JSON直接转成对应的类别

    原文:[Visual Studio]透过Visual Studio 2012的选择性贴上将XML与JSON直接转成对应的类别 在开发专案时若碰到要串接服务或是他人的API,常常避免不了都要面对XML或 ...