【C++】你不得不爱的——继承
凡是面向对象的语言,都有三大特性,继承,封装和多态,但并不是只有这三个特性,是因为者三个特性是最重要的特性,那今天我们一起来看继承!
目录
1.继承的概念及定义
1.概念
2.继承的定义
2.基类和派生类对象赋值转换
3.继承中的作用域
4.派生类的默认成员函数
1.构造和拷贝构造,赋值
2.析构函数的两怪!
5.继承与友元
6. 继承与静态成员
7.多继承
7.1继承分类
7.2 菱形继承 &&菱形虚拟继承
1.解决二义性的过程(指定作用域)
2.解决数据冗余(需要虚继承)
8.继承和组合(都是一种复用)
总结:
1.继承的概念及定义
1.概念
class Person { protected:char* _name;int _old; };class student :public Person { private:char* _id; }; int main() {student s; }
当好多类都需要写Person类中的成员时 ,为了避免数据冗余,就可以使用类的继承,使代码复用,继承是让每一个派生类中,都有一份基类(父类)的成员。
2.继承的定义
继承的方式:(当然继承的目的就是为了让子类可以拥有父类的成员,并访问,所以一般情况下,我们只会进行公有继承:public:)
那么我们来看一下,继承方式和访问之间的关系:
首先必须知道的一点是:基类中有私有成员时,子类中继承的父类的私有成员不可见。
(不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。)
其次就是,基类成员的权限和继承方式的权限,谁的权限更小,在子类中继承的成员就是更小的那个权限。public > protected> private。
如上图所示:
2.基类和派生类对象赋值转换
首先得回想起赋值转换这个过程:
不同的类型相互赋值时,中间会产生临时变量,通过临时变量进行赋值转换。
但是若子类和父类进行赋值交换时,并不产生中间的临时变量,而是天然的一个赋值。
(只能向上转换,即子类赋值给父类,字可以给父,父不可以给子)
看下面代码:
class Person
{
protected:string _name; // 姓名string _sex; // 性别
public:int _age; // 年龄
};class Student : public Person
{
public:int _No; // 学号
};int main()
{int i = 1;double d = 2.2;//中间会产生一个临时变量,临时变量具有常性,不可以改变。i = d;//所以此时ri是中间的临时变量的引用,而不是d的引用,如果不加const,就会放大权限const int& ri = d;//但父类和子类之间的赋值就不会产生中间的临时变量Person p;Student s;// 中间不存在类型转换,天然的一个赋值p = s;Person& rp = s;//对s的引用,可以访问和修改成员变量rp._age = 1;Person* ptrp = &s;ptrp->_age++;return 0;
}

只能向上转换,即子类赋值给父类,字可以给父,父不可以给子
3.继承中的作用域
我们知道,一个类他就是一个域(作用域)。同一作用域不能定义同名的两个变量,但不同作用域它可以定义两个同名变量。所以父类,子类中都有同名的成员变量时,默认会自动访问子类的成员,因为就近原则,若想访问父类的成员,那就可以指定作用域!
同一作用域,定义两个同名函数,且参数不同叫做函数重载;
不同作用域,定义两个同名函数,叫做重定义;
举例说明:
class A
{
public:void fun(){cout << "A::func()" << endl;}
};class B : public A
{
public:void fun(int i){cout << "B::func(int i)->" << i << endl;}
};void Test()
{B b;b.fun(10);
};
这中情况,函数构成什么???函数重载?? 重定义(隐藏)??编译错误???
首先我们知道两个类中的同名函数,在不同作用域,这就构成了重定义(隐藏)!
若访问父类成员函数即:b.A::fun();
4.派生类的默认成员函数
1.构造和拷贝构造,赋值
先回顾一下,默认成员函数(无参,全缺省,编译器自己生成)
具体分析:
class Person //父类
{
public:Person(const char* name) : _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};class Student : public Person //子类
{
public:Student(const char*name, int num)
//子类显示构造时,父类不可以直接访问进行初始化,必须调用父类自己的显示构造函数:Person(name) , _num(num){}Student(const Student& s)
//子类显示拷贝构造时,父类不可以直接访问进行初始化,必须调用父类自己的显示拷贝构造函数:Person(s), _num(s._num){}Student& operator=(const Student& s){if (this != &s){
//赋值的运算符重载,两个同名函数构成了隐藏,需要指定作用域Person::operator=(s);_num = s._num;}return *this;}protected:int _num; //学号
};int main()
{Student s1("张三", 18);Student s2(s1);Student s3("李四", 20);s1 = s3;return 0;
}
最重要的一句话:父类成员必须调用父类自己的构造函数,拷贝构造完成初始化或拷贝。
或者说:子类中的父类那部分成员由父类自己的构造或者拷贝构造实现初始化或者拷贝。
2.析构函数的两怪!
直接看代码:
class Person
{
public:Person(const char* name = " "): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;delete[] p;}protected:string _name; // 姓名int* p = new int[10];
};class Student : public Person
{
public:Student(const char* name):Person(name), _num(1){}Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator= (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator =(s);_num = s._num;}return *this;}// 第一怪:1、子类析构函数和父类析构函数构成隐藏关系。(由于多态关系需求,所有析构函数都会特殊处理成destructor函数名)// 第二怪:子类先析构,父类再析构。子类析构函数不需要显示调用父类析构,子类析构后会自动调用父类析构~Student(){//Person::~Person();cout << "~Student()" << endl;}protected:int _num; //学号
};int main()
{Student s("张三");return 0;
}
1、子类析构函数和父类析构函数构成隐藏关系。(由于多态关系需求,所有析构函数都会特殊处理成destructor函数名)
2.子类先析构,父类再析构。子类析构函数不需要显示调用父类析构,子类析构后会自动调用父类析构
构造顺序:先父类,再子类;析构顺序:先子类,再父类。
5.继承与友元

若子类对象也想访问友元函数,那只能在子类中也加上友元!(但不建议使用友元,会破坏继承关系)
6. 继承与静态成员
子类继承父类,不是继承父类这个对象,而是会有一份父类的模型。父类有的成员变量,子类也会有一份,互不干扰。
但静态成员就不一样了,他们是同一份;静态成员属于整个类和类的所有对象。同时也属于所有派生类及派生类的对象。
class Person { public://friend void Display(const Person& p, const Student& s); protected:string _name; // 姓名 public:static int _num; }; int Person::_num = 0;class Student : public Person { protected:int _stuNum; // 学号 };void test() {Student s;Person p;cout << p._num << endl;p._num++;cout << p._num << endl;s._num++;cout << s._num << endl;cout << &s._num << endl;cout << &p._num << endl; }
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例重点:Person* ptr = nullptr;ptr->Print();cout << ptr->_num << endl;cout << ptr->_name << endl;cout << (*ptr)._num << endl;(*ptr).Print();
对象里面只存成员变量,成员函数在代码段中,所以以上代码哪个不对呢?
我们知道空指针不能解引用,解引用意思是,这里是去访问指针指向对象的内部成员,那看一看哪个访问了内部的成员呢?
函数不在内部,在代码段,可以!
_num为对象内部成员变量,不能解引用访问,不可以!
(*ptr)是解引用了吗?我们不能凭借解引用符号来判断是否解引用,我们需要看内部的访问情况,(*ptr)->Print();并没有访问内部成员,可以!
(*ptr)->_num;也可以,_num是静态成员,不在成员里面。
7.多继承
7.1继承分类
单继承:一个子类只有一个直接父类
多继承:一个子类有两个或两个以上的父类
菱形继承:是多继承的一种特殊情况,会产生数据冗余和二义性!
(person类的中的成员,会在student和teacher中都有一份,assistant继承student和teacher时,assistant中会有两份person,造成了数据冗余和二义性)
解决方法:
解决二义性:
可以通过访问限定符来指定访问哪一个成员。
那如何解决二义性的问题呢?
此时虚继承就上线了!
虚继承在腰部继承,谁引发的数据冗余,谁就进行虚继承(解决冗余)
由此可见,加上virtual,变为虚继承以后,确实解决了数据的冗余
那么到底如何解决的呢??具体下面分析!
7.2 菱形继承 &&菱形虚拟继承
1.解决二义性的过程(指定作用域)
(菱形继承)class A { public:int _a; };class B:public A { public:int _b; };class C:public A { public:int _c; };class D:public B,public C { public:int _d; };int main() {D d;d._b = 1;d._c = 2;d._d = 3;d.B::_a = 4;d.C::_a = 5; }
2.解决数据冗余(需要虚继承)
(菱形虚拟继承)
class A { public:int _a; };class B:virtual public A { public:int _b; };class C:virtual public A { public:int _c; };class D:public B,public C { public:int _d; };int main() {D d;d._b = 1;d._c = 2;d._d = 3;d.B::_a = 4;d.C::_a = 5; }
那如果遇到这种情况呢???父子类的赋值转换(切片)
class A { public:int _a; };class B:virtual public A { public:int _b; };class C:virtual public A { public:int _c; };class D:public B,public C { public:int _d; };int main() {D d;d._b = 1;d._c = 2;d._d = 3;d._a = 4;d._a = 5;B b;b._a = 1;b._b = 3;B* ptr = &b;ptr->_a = 2;ptr = &d;ptr->_a = 6; }
从b对象可以看的出来,只要是虚继承以后,就会把虚基类放到最下面;
就像切片这种情况,ptr指向不同,那么距离虚基类的距离就不同,所以就必须要有虚基表的地址,来访问虚基表继而找到偏移量,然后访问到虚基类!
我们通常使用下,很忌讳出现菱形继承,但可以多继承。
可以看得出,虚继承在时间上确实有损耗,过程比较复杂,但是如果虚基类比较大时,就可以很大程度上节省内存。
8.继承和组合(都是一种复用)
总结:
一口气说了这么多,你学会了吗?细节还是比较多的,我们应该下去多多自己琢磨,反复调试,去感受过程,从而理解的更深刻!下期再见!
相关文章:

【C++】你不得不爱的——继承
凡是面向对象的语言,都有三大特性,继承,封装和多态,但并不是只有这三个特性,是因为者三个特性是最重要的特性,那今天我们一起来看继承! 目录 1.继承的概念及定义 1.概念 2.继承的定义 2.基类…...

数据库系统概论
文章目录前言基础篇:1-5章第 1 章 绪论1.1 数据库系统概述1.2 数据模型1.3 数据库系统的结构1.4 数据库系统的组成1.5 小结第 2 章 关系数据库1.关系模型1.1 关系数据结构1.2 关系完整性约束实体完整性、参照完整性、用户定义完整性2.关系代数8种关系代数运算符并 ∪…...

32位处理器AM6528BACDXEA、AM6548BACDXEAF基于Arm Cortex-A53内核【工业4.0嵌入式产品应用】
AM6528BACDXEA、AM6548BACDXEAF 处理器是专为满足工业4.0嵌入式产品对处理性能的复杂需求而设计的Arm应用处理器。AM654x和AM652x器件将四个或两个Arm Cortex-A53内核与一个双Arm Cortex-R5F MCU子系统结合在一起。这些包含的功能旨在帮助客户实现终端产品的功能安全目标。它还…...

多图片怎么转换成PDF?这招教你轻松转换
多图片怎么转换成PDF?我们经常会传输图片文件给同事或者朋友,但是多张图片的传输比较麻烦,有的时候传输比较慢,而且也不便于查看,所以我们就可以将需要传输的多张图片转换成一个PDF文件,这样查看文件时就可…...

kali双网卡
先单独开启一个网卡,配置/etc/network/interfaces 修改为如下配置 This file describes the network interfaces available on your system and how to activate them. For more information, see interfaces(5). source /etc/network/interfaces.d/* The loopb…...

【wed前端初级课程】第一章 什么是HTML
什么是WEB前端? 简单来说就是网页,只是这个网页它是由多种技术参与制作的,用来向用户展示的页面。 HTML(超文本标签语言):它决定了网页的结构。 CSS:网页的装饰器。 JavaScript:JavaScrip最初是因为校验…...

sd卡格式化后数据恢复怎么操作
有时候我们需要清空SD卡数据文件,有时候则是因为需要修复SD卡所以需要格式化,但是却被提示无法格式化SD卡。这种情况往往是由于平时SD卡使用时的一些不良习惯或是SD卡中病毒,病毒在运行SD卡中的软件所造成的。那么sd卡格式化后数据恢复怎么操…...

论文阅读笔记|大规模多标签文本分类
多标签文本分类(Extreme Multi Label Classification, MLTC)是自然语言处理领域中一个十分重要的任务,其旨在从一个给定的标签集合中选取出与文本相关的若干个标签。MLTC可以广泛应用于网页标注,话题识别和情感分析等场景。大规模…...
国际化翻译navigator.language与语种对照表
代码(navigator.language) 语种 字段名 "zh-CN", 中文 Chinese "zh-SG" 马新简体 Chinese_SG "zh-TW","zh-HK", 繁体中文 Chinese_TW "en", "en-US"(美国), "en-EG"…...

Matlab进阶绘图第6期—雷达图/蜘蛛图/星图
雷达图(Radar Chart),又称星图、蜘蛛图、蜘蛛网图、网络图、Kiviat图等,是一种以从同一点开始的轴上表示的三个以上变量的二维图表的形式,来显示多变量数据的图形方法。 雷达图可以直观地对多维数据集目标对象的性能、…...

Javascript的ES6 class写法和ES5闭包写法性能对比
看到很多闭包写法的函数, 一直怀疑它对性能是否有影响. 还有就是备受推崇的React Hooks函数式写法中出现大量的闭包和临时函数, 我很担心这样会影响性能. 于是, 做了一个实验来做对比. 这个实验很简单, 用md5计算一百万次. 计算过程将结果再放回参数, 这样避免结果没被引用被…...

探秘MySQL——全面了解索引、索引优化规则
文章目录0.什么是索引1.常用索引分类逻辑维度底层数据结构维度物理维度(InnoDB)2.为什么底层是B树平衡二叉查找树红黑树B树(多叉)B树(多叉)3.MySQL索引优化SQL性能分析之explainQ.MySQL如何查看查询是否用到…...

战斗力最强排行榜:10-30人团队任务管理工具
工欲善其事,必先利其器。在高效的任务执行过程中,选择灵活轻便的项目管理工具来提升工作效率、适应快速多变的发展诉求,对团队来说,至关重要。但是如果团队不大,企业对这块的预算又有限,大型的团队任务管理…...

2023-03-09干活小计
强化学习: 强化学习用智能体(agent)这个概念来表示做决策的机器。 感知、决策和奖励 感知。智能体在…...

基数排序算法
目录:什么是基数排序?基本原理核心思想实现逻辑代码实现复杂度分析总结什么是基数排序? 基数排序:基数排序(Radix sort)是一种非比较型整数排序算法, 基本思想主要是通过关键字间的比较和移动记…...

项目实战典型案例24——xxljob控制台不打印日志排查
xxljob控制台不打印日志排查一:背景介绍问题截图问题解读二:思路&方案三:过程四:总结一:背景介绍 本篇博客是对xxljob控制台不打印日志排查进行的总结和进行的改进。 目的是将经历转变为自己的经验。通过博客的方…...

旋转框目标检测mmrotate v1.0.0rc1 之RTMDet训练DOTA的官方问题解析整理(四)
关于rotated_rtmdet_l-coco_pretrain-3x-dota_ms.py配置文件的batchsize和学习率设置问题:回答:如何在mmrotate中绘制特征图问题:回答:你好AllieLan,您可以尝试使用https://github.com/open-mmlab/mmyolo/blob/main/de…...

4个顶级的华为/小米/OPPO/Vivo手机屏幕解锁工具软件
有好几次用户发现自己被锁定在他们的华为/小米/OPPO/Vivo设备之外,我们知道这可能是一种非常可怕的体验。在这种情况下,找到安卓手机解锁软件,重新获得手机中重要数据和文件的访问权限。看看这篇文章,因为我们将与您分享什么是解锁…...
华为OD机试题 - 和最大子矩阵(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:和最大子矩阵题目输入输出示例一输入输出说明Code思路版权说明华…...
企业电子招标采购系统源码之项目说明和开发类型
项目说明 随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范,以及…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...