c++ primer plus学习笔记(7)——类继承
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)——类继承相关推荐
- Python 学习笔记13 类 - 继承
我们在编程的过程中,并非都是要重头开始.比如其他人已经有现成的类,我们可以使用其他找人编写的类.术语称之为: 继承. 当一个类继承例外一个类时,它可以获得这个类的所有属性和方法:原有的类称之为 父类, ...
- python编程语言继承_python应用:学习笔记(Python继承)
学习笔记(Python继承)Python是一种解释型脚本语言,可以应用于以下领域: web 和 Internet开发 科学计算和统计 人工智能 教育 桌面界面开发 后端开发 网络爬虫 有几种叫法(父类 ...
- ASM学习笔记2 - 类的创建和修改 —— ClassWriter的综合应用
ASM学习笔记2 - 类的创建和修改 -- ClassWriter的综合应用 上回我们说到,通过使用ClassVisitor和ClassReader,我们能够分析已经存在的类.这一节中,我们将使用Cl ...
- Python学习笔记 (类与对象)
Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...
- C++ Primer Plus 学习笔记(第 4 章 复合类型)
C++ Primer Plus 学习笔记 第 4 章 复合类型 数组 数组(array)是一种数据格式,能够存储多个同类型的值. 要创建数组,可使用声明语句.数组声明应指出以下三点: 存储在每个元素的 ...
- python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程
一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...
- 整理:C primer plus 学习笔记
前言:简单看了一遍C Primer Plus, 整理了一下,因为时间比较少,自己理解地比较肤浅,所以第一版比较简陋. 假期的时候应该会有时间再整理一下.------2018/11/5 2019/1/2 ...
- Machine Learning A-Z学习笔记12-分类模型性能评级及选择
Machine Learning A-Z学习笔记12-分类模型性能评级及选择 1.简单原理 一般认为假阴性比假阳性更严重,如核酸检测 用混淆矩阵表示如下图 准确率驳论(Accuracy Paradox ...
- JAVA学习笔记(类的学习)
JAVA学习笔记(类的学习) 类声明和类体 构造方法和对象创建 对象的引用和实体 成员变量 方法 方法重载 关键字this 包 import语句 访问权限 对象数组 反编译和文档生成器 JAR文件 1 ...
- java学习笔记6--类的继承、Object类
接着前面的学习: java学习笔记5--类的方法 java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) java学习笔记2--数据类型.数组 java学习笔记 ...
最新文章
- 中国CIO最关心的八大问题(下)
- oracle 删除补全日志组_【REDO】删除REDO LOG重做日志组后需要手工删除对应的日志文件(转)...
- 用了那么多年的 Master 分支或因种族歧视而成为历史?
- 洛谷——P1031 均分纸牌
- 实现贝叶斯分类器_机器学习实战项目-朴素贝叶斯
- leetcode421. 数组中两个数的最大异或值(贪心算法)
- 复杂性思维中文第二版 六、生命游戏
- 1086 就不告诉你 (15 分)—PAT (Basic Level) Practice (中文)
- jdbc增删改查_JDBC和MyBaits之争,Debug告诉你谁更胜一筹
- Linux下编写GT911触摸驱动
- 2017年网络小说人气排行榜
- 对偶式与反函数_图解数字电路中标准式的对偶式和反函数求解
- Python计算均值、方差、标准差、协方差等常用指标的方法——Numpy模块+Pandas模块
- CF1267G Game Relics(期望、背包)
- matlab计算轮廓曲率半径,【转】求最小曲率半径matlab源程序
- 中国驾照在美国各州开车的规定
- 怎么把英文翻译成中文?手机中英翻译的简单方法
- 【Django】有效解决django.core.exceptions.ImproperlyConfigured: Requested setting EMAIL_FROM, but settings
- Ansible中的常用模块介绍
- 什么是单页面应用开发?
热门文章
- 加密与解密(一) -- 壳、加壳
- 删除OneDrive右键菜单
- java中倒三角形和正三角形_正三角形,倒三角形,以及正倒三角
- Ngxin虚拟主机的三种配置方法
- 【支持升级官方最新版】西部数码主机代理系统模板源码IDC网站源码虚拟主机代理管理系统
- 【工具-SublimeText3】在SublimeText3中无法高亮 .vue 文件内容和less代码的解决方案
- 2022「博客新星」年度评选TOP100名单
- vc 热键、组合键的用法 MFC c++ hotkey WM_HOTKEY
- package.json browserslist
- 针对ABCmouse的Xadmin管理端使用探究手册