2.1多继承

在这一节,我们将围绕构造函数的发生、方法的二义性进行讨论

2.1.1构造、析构顺序:栈原则

假如一个test类继承两个基类:base1、base2,下述代码显示了他们的构造顺序。

#include <iostream>
using namespace std;
class Base1
{
public:Base1() { cout << "Base1 constructor\n"; };~Base1() { cout << "Base1 destructor\n"; };
};class Base2
{
public:Base2() { cout << "Base2 constructor\n"; };~Base2() { cout << "Base2 destructor\n"; };
};class Test : public Base1, public Base2
{
public:Test() : Base1(), Base2() { cout << "Test constructor\n"; };~Test() { cout << "Test destructor\n"; };
};int main()
{Test a;
}

输出:

Base1 constructor
Base2 constructor
Test constructor
Test destructor
Base2 destructor
Base1 destructor


如果将test的构造函数这样定义,即先写base2,再写base1,输入仍然不变。这说明基类的构造函数是按照声明顺序调用的。

class Test : public Base1, public Base2
{
public:Test() : Base2(), Base1() { cout << "Test constructor\n"; };……
}

复杂的例:一个worker类分别派生出两个类:singer类、waiter类,以表示歌手和侍者的信息。这两个类又派生出一个类:SingerWaiter类。

#include <iostream>
using namespace std;
class Worker
{
public:Worker() {cout << "Worker constructor\n"; };~Worker() {cout << "Worker destructor\n"; };void show() { cout << "show called\n"; };
};class Singer : public Worker
{
public:Singer() { cout << "Singer constructor\n"; };~Singer() { cout << "Singer destructor\n"; };
};class Waiter : public Worker
{
public:Waiter() { cout << "Waiter constructor\n"; };~Waiter() { cout << "Waiter destructor\n"; };
};class SingerWaiter : public Singer, public Waiter
{
public:SingerWaiter() : Singer(), Waiter() { cout << "SingerWaiter constructor\n"; };~SingerWaiter() { cout << "SingerWaiter destructor\n"; };
};int main()
{SingerWaiter x;
}

输出:

Worker constructor
Singer constructor
Worker constructor
Waiter constructor
SingerWaiter constructor
SingerWaiter destructor
Waiter destructor
Worker destructor
Singer destructor
Worker destructor


2.1.2方法二义性

如前述复杂的例,若调用show方法,将调用哪个worker类的show方法?

int main()
{SingerWaiter x;x.show();
}

编译器报错:

test.cpp: In function 'int main()':
test.cpp:59:7: error: request for member 'show' is ambiguous
     x.show();


解决方法:使用作用域运算符,如下述代码将调用singer类的show方法

int main()
{SingerWaiter x;x.Singer::show();
}

2.2虚继承

一个典型的问题:我们为什么需要两个worker类?

为了解决这个问题,c++引入虚继承。在继承列表使用关键字virtual,使得继承变成虚继承,其基类为虚基类。

2.2.1构造函数、析构函数

  • 虚继承的类被多重继承时,其虚基类是共享的。
  • c++禁止信息通过中间类传递给虚基类,我们需要直接使用虚基类的构造函数。如:
#include <iostream>
using namespace std;
class Worker
{
public:Worker() { cout << "Worker constructor\n"; };~Worker() { cout << "Worker destructor\n"; };void show() { cout << "show called\n"; };
};class Singer : virtual public Worker
{
public:Singer() { cout << "Singer constructor\n"; };~Singer() { cout << "Singer destructor\n"; };
};class Waiter : virtual public Worker
{
public:Waiter() { cout << "Waiter constructor\n"; };~Waiter() { cout << "Waiter destructor\n"; };
};class SingerWaiter : public Singer, public Waiter
{
public:SingerWaiter() : Worker(), Singer(), Waiter() { cout << "SingerWaiter constructor\n"; };~SingerWaiter() { cout << "SingerWaiter destructor\n"; };
};int main()
{SingerWaiter x;x.show();
}

输出:

Worker constructor
Singer constructor
Waiter constructor
SingerWaiter constructor
show called
SingerWaiter destructor
Waiter destructor
Singer destructor
Worker destructor


worker只被构造一次,这表明singer和waiter共同使用一个worker类。

2.2.2二义性

一个典型的问题:上述输出中哪个show被调用?

如果没有重定义show方法,虚继承将不产生问题,解决了普通继承的缺点。但是如下例,若在中间类重新定义了show方法:

#include <iostream>
using namespace std;
class Worker
{
public:void show() { cout << "show called\n"; };
};class Singer : virtual public Worker
{
public:void show() { cout << "show1 called\n"; };
};class Waiter : virtual public Worker
{
public:void show() { cout << "show2 called\n"; };
};class SingerWaiter : public Singer, public Waiter {……};int main()
{SingerWaiter x;x.show();
}

编译器报错:

test.cpp: In function 'int main()':
test.cpp:37:7: error: request for member 'show' is ambiguous
     x.show();


修复方法1:使用作用域运算符

int main()
{SingerWaiter x;x.Singer::show();
}

在最近处(SingerWaiter)重定义show方法

class SingerWaiter : public Singer, public Waiter
{
public:void show() { cout << "show3 called\n"; };
};int main()
{SingerWaiter x;x.show();
}

2.3方法匹配:最近原则

多重继承时,若直接基类有重名函数,

  • 普通继承一定引发二义性;
  • 虚继承不一定引发二义性。其匹配原则是派生类优先于基类或间接基类

如:若只在Singer类中重声明show方法,则Singer类中的show方法优先于Worker中的,不产生二义性。

#include <iostream>
using namespace std;
class Worker
{
public:void show() { cout << "Worker called\n"; };
};class Singer : virtual public Worker
{
public:void show() { cout << "Singer called\n"; };
};class Waiter : virtual public Worker {……};class SingerWaiter : public Singer, public Waiter {……};int main()
{SingerWaiter x;x.show();
}

2.4混合使用虚继承与普通继承

如下述代码,Base虚派生出D1,D2,派生出D3,M继承这三个中间类。则M中有两个Bae类D1、D2的共同Bas类,和D3中包含的Base类。

#include <iostream>
using namespace std;
class Base
{
public:Base() { cout << "Base constructor\n"; };~Base() { cout << "Base destructor\n"; };
};class D1 : virtual public Base
{
public:D1() { cout << "D1 constructor\n"; };~D1() { cout << "D1 destructor\n"; };
};class D2 : virtual public Base
{
public:D2() { cout << "D2 constructor\n"; };~D2() { cout << "D2 destructor\n"; };
};class D3 : public Base
{
public:D3() { cout << "D3 constructor\n"; };~D3() { cout << "D3 destructor\n"; };
};class M : public D1, public D2, public D3
{
public:M() : Base(), D1(), D2(), D3() { cout << "M constructor\n"; };~M() { cout << "M destructor\n"; };
};int main()
{M x;
}

输出:

Base constructor
D1 constructor
D2 constructor
Base constructor
D3 constructor
M constructor
M destructor
D3 destructor
Base destructor
D2 destructor
D1 destructor
Base destructor


3.类模板

c++ primer plus学习笔记(7)——类继承相关推荐

  1. Python 学习笔记13 类 - 继承

    我们在编程的过程中,并非都是要重头开始.比如其他人已经有现成的类,我们可以使用其他找人编写的类.术语称之为: 继承. 当一个类继承例外一个类时,它可以获得这个类的所有属性和方法:原有的类称之为 父类, ...

  2. python编程语言继承_python应用:学习笔记(Python继承)

    学习笔记(Python继承)Python是一种解释型脚本语言,可以应用于以下领域: web 和 Internet开发 科学计算和统计 人工智能 教育 桌面界面开发 后端开发 网络爬虫 有几种叫法(父类 ...

  3. ASM学习笔记2 - 类的创建和修改 —— ClassWriter的综合应用

    ASM学习笔记2 - 类的创建和修改 -- ClassWriter的综合应用 上回我们说到,通过使用ClassVisitor和ClassReader,我们能够分析已经存在的类.这一节中,我们将使用Cl ...

  4. Python学习笔记 (类与对象)

    Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...

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

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

  6. python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程

    一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...

  7. 整理:C primer plus 学习笔记

    前言:简单看了一遍C Primer Plus, 整理了一下,因为时间比较少,自己理解地比较肤浅,所以第一版比较简陋. 假期的时候应该会有时间再整理一下.------2018/11/5 2019/1/2 ...

  8. Machine Learning A-Z学习笔记12-分类模型性能评级及选择

    Machine Learning A-Z学习笔记12-分类模型性能评级及选择 1.简单原理 一般认为假阴性比假阳性更严重,如核酸检测 用混淆矩阵表示如下图 准确率驳论(Accuracy Paradox ...

  9. JAVA学习笔记(类的学习)

    JAVA学习笔记(类的学习) 类声明和类体 构造方法和对象创建 对象的引用和实体 成员变量 方法 方法重载 关键字this 包 import语句 访问权限 对象数组 反编译和文档生成器 JAR文件 1 ...

  10. java学习笔记6--类的继承、Object类

    接着前面的学习: java学习笔记5--类的方法 java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) java学习笔记2--数据类型.数组 java学习笔记 ...

最新文章

  1. 中国CIO最关心的八大问题(下)
  2. oracle 删除补全日志组_【REDO】删除REDO LOG重做日志组后需要手工删除对应的日志文件(转)...
  3. 用了那么多年的 Master 分支或因种族歧视而成为历史?
  4. 洛谷——P1031 均分纸牌
  5. 实现贝叶斯分类器_机器学习实战项目-朴素贝叶斯
  6. leetcode421. 数组中两个数的最大异或值(贪心算法)
  7. 复杂性思维中文第二版 六、生命游戏
  8. 1086 就不告诉你 (15 分)—PAT (Basic Level) Practice (中文)
  9. jdbc增删改查_JDBC和MyBaits之争,Debug告诉你谁更胜一筹
  10. Linux下编写GT911触摸驱动
  11. 2017年网络小说人气排行榜
  12. 对偶式与反函数_图解数字电路中标准式的对偶式和反函数求解
  13. Python计算均值、方差、标准差、协方差等常用指标的方法——Numpy模块+Pandas模块
  14. CF1267G Game Relics(期望、背包)
  15. matlab计算轮廓曲率半径,【转】求最小曲率半径matlab源程序
  16. 中国驾照在美国各州开车的规定
  17. 怎么把英文翻译成中文?手机中英翻译的简单方法
  18. 【Django】有效解决django.core.exceptions.ImproperlyConfigured: Requested setting EMAIL_FROM, but settings
  19. Ansible中的常用模块介绍
  20. 什么是单页面应用开发?

热门文章

  1. 加密与解密(一) -- 壳、加壳
  2. 删除OneDrive右键菜单
  3. java中倒三角形和正三角形_正三角形,倒三角形,以及正倒三角
  4. Ngxin虚拟主机的三种配置方法
  5. 【支持升级官方最新版】西部数码主机代理系统模板源码IDC网站源码虚拟主机代理管理系统
  6. 【工具-SublimeText3】在SublimeText3中无法高亮 .vue 文件内容和less代码的解决方案
  7. 2022「博客新星」年度评选TOP100名单
  8. vc 热键、组合键的用法 MFC c++ hotkey WM_HOTKEY
  9. package.json browserslist
  10. 针对ABCmouse的Xadmin管理端使用探究手册