代码重用(has-a)

  • 包含、组合或层次化
  • 私有继承
  • 包含 or 私有继承?
  • 各种继承方式
  • 多重继承
  • 类模板

C++的一个主要目标是促进代码重用。公有继承是实现这种目标的机制之一,但并不是唯一的机制。还有一种方式是使用这样的类成员:本身是另一个类的对象。这种方法称为包含(containment)、组合(composition)或层次化(layering)。另一种方法是使用私有或保护继承。通常,包含、私有继承和保护继承用于实现has-a关系,即新的类将包含另一个类的对象。

包含、组合或层次化

使用公有继承时,类可以继承接口,可能还有实现(基类的纯虚函数提供接口,但不提供实现)。获得接口是is-a关系的组成部分。而使用组合,类可以获得实现,但不能获得接口。不继承接口是has-a关系的组成部分。

私有继承

使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会成为派生类对象公有接口的一部分,但可以在派生类的成员函数中使用它们。因此私有继承提供的特性与包含相同:获得实现,但不获得接口。

包含 or 私有继承?

通常,应使用包含来建立has-a关系,有两个原因:1. 它易于理解,类声明中包含表示被包含类的显式命名对象,代码可以通过名称引用这些对象,而使用继承将使关系更抽象。 2. 继承会引起很多问题,尤其从多个基类继承时,可能必须处理很多问题。

但是如果新类需要访问原有类的保护成员,或需要重新定义虚函数,则应使用私有继承。

各种继承方式

特征 公有继承 保护继承 私有继承
公有成员变成 派生类的公有成员 派生类的保护成员 派生类的私有成员
保护成员变成 派生类的保护成员 派生类的保护成员 派生类的私有成员
私有成员变成 只能通过基类接口访问 只能通过基类接口访问 只能通过基类接口访问
能否隐式向上转换 是(但只能在派生类中)

多重继承

  • 定义
    使用多个基类的继承被称为多重继承(multiple inheritance, MI)。MI描述的是有多个直接基类的类。与单继承一样,公有MI表示的也是is-a关系。

  • 二义性
    比如,从Singer和Waiter公有派生出SingingWaiter,因为Singer和Waiter都继承了一个worker组件,所以SingingWaiter将包含两个Worker组件,这将引起问题。通常可以将派生类对象的地址赋给基类指针,但现在将出现二义性:

    SingingWaiter ed;
    Worker * pw = &ed;  // ambiguous
    

    通常,这种赋值将把基类指针设置为派生对象中的基类对象的地址,但ed中包含两个Worker对象,有两个地址可供选择,所以应使用类型转换来指定对象:

    Worker * pw1 = (Waiter *) &ed;   // the worker in waiter
    Worker * pw2 = (Singer *) &ed;   // the worker in singer
    

    这将使得使用基类指针来引用不同的对象(多态性)复杂化。
    所以引入了虚基类(virtual base class) 这种技术来解决这种问题。

  • 虚基类
    虚基类 使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象。

  • 如果类有间接虚基类,则除非只需使用该虚基类的默认构造函数,否则必须显式地调用该虚基类的某个构造函数。

  • example
    假设没有在SingingWaiter类中重新定义show()方法,并试图使用SingingWaiter对象调用继承的show()方法。对于单继承,如果没有重新定义show(),则将使用最近祖先中的定义。而在多重继承中,每个直接祖先都有一个show()函数,这使得上述调用是二义性的。为了解决这个问题,可以使用作用域解析运算符来澄清编程者的意图:

    SingingWaiter newhire("Elis Hawks", 2005, 6, soprano);
    newhire.Show();     // 会出现二义性
    newhire.Singer::Show();   // use Singer version
    

    然而,更好的方法是在SingingWaiter中重新定义Show(),并指出要使用哪个Show()。

  • 虚基类和支配

    通过多种途径继承一个基类的时候,如果基类是虚基类,派生类将包含基类一个子对象;如果基类不是虚基类,派生类将包含多个子对象。使用非虚基类时,规则很简单。如果类从不同的类那里继承了两个或更多的同名成员(数据或方法),则使用该成员名时,如果没有用类名进行限定,将导致二义性。但如果使用的是基类,则这样做不一定会导致二义性。在这种情况下,如果某个名称优先于(dominates)其他所有名称,则使用它时,即便不使用限定符,也不会导致二义性。那么,一个成员名如有优先于灵一个成员名呢?派生类中的名称优先于直接或间接祖先类中的相同名称。

类模板

  • Why?

    类模板与函数模板的定义和使用类似,有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,我们可以通过如下面语句声明了一个类模板:

    template <typename T>
    class A
    {public:A(T t){this->t = t;}T& getT(){return t;}public:T t;
    };
    
  • How?

    类模板由模板说明和类说明构成。模板说明同函数模板,即:template <类型形式参数表>;类声明举例如下:

    template  <typename Type>class ClassName{public://ClassName 的成员函数
    private:Type DataMember;
    }
    
  • 单个类模板的使用

    #include <iostream>
    using namespace std;template <typename T>
    class A
    {public://函数的参数列表使用虚拟类型A(T t = 0){this->t = t;}//成员函数返回值使用虚拟类型T& getT(){return t;}private://成员变量使用虚拟类型T t;
    };void printA(A<int>& a) {cout << a.getT() << endl;
    }int main(void) {//1.模板类定义类对象,必须显示指定类型//2.模板种如果使用了构造函数,则遵守以前的类的构造函数的调用规则A<int>  a(666);cout << a.getT() << endl;//模板类做为函数参数printA(a);system("pause");return 0;
    }
    
  • 继承中类模板的使用

    1. 父类是一般类,子类是模板类

      class A {public:A(int temp = 0) {this->temp = temp;}~A(){}
      private:int temp;
      };template <typename T>
      class B :public A{public:B(T t = 0) :A(666) {this->t = t;}~B(){}
      private:T t;
      };
      

      父类是一般类,子类是模板类,和普通继承的用法没有区别。

    2. 子类是一般类,父类是模板类

      template <typename T>
      class A {public:A(T t = 0) {this->t = t;}~A(){}
      private:T t;
      };class B:public A<int> {public://也可以不显示指定,直接A(666)B(int temp = 0):A<int>(666) {this->temp = temp;}~B() {}
      private:int temp;
      };
      

      子类是一般类,父类是模板类,继承时必须在子类里实例化父类的类型参数。

    3. 父类和子类都是模板类

      父类和子类都是模板类时,子类的虚拟的类型可以传递到父类中。

C++ Primer Plus学习(十三)——代码重用(has-a)相关推荐

  1. OpenCV与图像处理学习十三——Harris角点检测(含代码)

    OpenCV与图像处理学习十三--Harris角点检测(含代码) 一.角点的概念 二.Harris角点检测的实现过程 三.Harris代码应用 一.角点的概念 角点: 在现实世界中, 角点对应于物体的 ...

  2. 【C++ Primer】第十四章 C++中的代码重用

    序:C++的一个主要目标是促进代码重用,其中包含公有继承.包含.使用私有或保护继承 一,包含对象成员的类        1)valarray类简介  #include <valarray> ...

  3. 一起来学C++:C++中的代码重用

    目录 14.1 包含对象成员的类 14.1.1 valarray类简介 14.1.2 Student类的设计 14.1.3 Student类示例 1.初始化被包含的对象 2.使用被包含对象的接口 3. ...

  4. 如何利用《C++ Primer》学习C++?

    <C++ Primer>作为久负盛名的C++经典教程,丰富的教学辅助内容.精心组织的编程示范,无论是初学者入门,或是中.高级程序员提升,都是不容置疑的首选. 一本好书只有读过才有价值,然而 ...

  5. C++ Primer Plus 学习记录(第五章节-包含练习题答案)

    C++ Primer Plus 学习记录(第五章节-包含练习题答案) 5.1 for循环 5.1.1 for循环的组成部分 1.for循环的组成部分所完成的步骤 2.赋值表达式有值 3.cout中显示 ...

  6. 番外篇--C++中的代码重用

    实现代码重用的一些方法(这里并不是全部): 包含(组合.层次化):类包含另一个类的对象 使用私有继承或保护继承 以上两种方法都用于实现has-a关系,常用第一种方法 多重继承可以使多个基类派生出一个新 ...

  7. C++ Primer Plus 学习笔记(第 4 章 复合类型)

    C++ Primer Plus 学习笔记 第 4 章 复合类型 数组 数组(array)是一种数据格式,能够存储多个同类型的值. 要创建数组,可使用声明语句.数组声明应指出以下三点: 存储在每个元素的 ...

  8. 用Eclipse的snippets功能实现代码重用

    snippets功能实现代码重用 Snippets 代码片段是Eclipse的一个插件. 很多时候可以通过这个功能,重复使用常用的代码片段,加快开发效率. 创建一个代码段的步骤: 在Eclipse的e ...

  9. 如何阅读一份深度学习项目代码?

    犹豫很久要不要把读代码这个事情专门挑出来写成一篇推文.毕竟读代码嘛,大家可能都会读.而且笔者个人读的和写的代码量也并不足以到指导大家读代码的程度.但笔者还是决定大胆地写一点:就当是给自己设立今后读代码 ...

  10. 准确率可提升50%以上,Facebook用迁移学习改进代码自动补全

    视学算法报道 转载自:机器之心 编辑:陈萍.杜伟 来自 Facebook 的研究团队将迁移学习用于代码自动补全,提出的方法在非常小的微调数据集上提高 50% 以上的准确率,在 50k 标记示例上提高了 ...

最新文章

  1. STARTUPE2原语
  2. linux中pthread_kill函数详解
  3. java 对比度,java批改图片亮度对比度
  4. Thunder团队Beta周贡献分分配结果
  5. 最后一个单词的长度Python解法
  6. python turtle工具箱_python 库之 turtle(图形绘制) 开启新的快乐源泉
  7. 常用的排序算法总结(二)
  8. 自动化测试,面试【必备题】
  9. freebsd下fcgi程序例子
  10. 17 java 存在的问题(转)
  11. linux vi文件提示swp,如何解决非正常关闭vi编辑器时生成.swp文件问题
  12. 达梦DCA学习笔记202004
  13. 成都锦里VS宽窄巷子
  14. 家里wifi密码忘了怎么办?
  15. 移植中文TTS(ekho)到ARM linux开发板
  16. 下载的网页浏览器打开一直跳转问题
  17. 留学目的地之马里兰州
  18. pyqt pyside2 QLabel 显示图片问题
  19. 基于荔枝派Lichee Nano(全志f1c100s)的kernel移植(二)
  20. 计算机CMOS设置详解

热门文章

  1. matplotlib绘制图像设置中文宋体,英文新罗马,字体大小7.5,坐标轴刻度线内侧
  2. python暂停命令_命令行-Python中的暂停
  3. Tomcat中Session钝化与活化实现步骤
  4. Ubuntu 15.04 搜狗输入法 无法切换到英文输入
  5. 秒拍视频其实在你浏览器缓存里
  6. mantis使用介绍
  7. 淘宝模拟登录2解决滑动验证问题
  8. 罗振宇2021“时间的朋友·长大以后”跨年演讲全文无删减整理,核心观点、大纲提炼
  9. 使用Apache Tika实现内容分析
  10. C语言10进制转2进制