C++学习第十二天(继承)
1、继承的概念以及定义
继承的概念
继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行拓展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
class Person
{
public:void Print(){cout << "name:" << _name << endl;}
protected:string _name = "peter";//姓名int _age = 18; //年龄
};//继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
//student和teacher复用了Person的成员。
class Student : public Person
{
protected:int _studentid;
};class Teacher : public Person
{
protected:int _workid;
};int main()
{Student s;Teacher t;s.Print();t.Print();return 0;
}
继承定义
定义的格式
class Student : public Person
{ //派生类 继承方式 基类
public:int _stuid;int _major;
}
继承关系和访问限定符
| 继承方式 | public继承 | protected继承 | private继承 |
| 访问限定符 | public访问 | protected访问 | private访问 |
继承基类成员访问方式的变化
| 类成员/继承方式 | public继承 | protected继承 | private继承 |
| 基类的public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private成员 |
| 基类的protected成员 | 派生类的protected成员 | 派生类的protected成员 | 派生类的private成员 |
| 基类的private成员 | 在派生类不可见 | 在派生类不可见 | 在派生类不可见 |
总结:
- 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能访问它
- 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类被访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
- 实际上面的表格我们可以发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式为 min(成员在基类的访问限定符,继承方式),public > protected > private
- 使用关键字class默认继承方式是private,使用struct的默认继承方式是public,所以最好写出继承方式
- 实际上一般使用public继承,很少使用protected或者是private继承
class Person { public:void Print(){cout << _name << endl;} protected:string _name = "father"; private:int _age; }; //class Student : public Person //class Student : protected Person class Student : private Person { public:void he(){Print();} protected:int _stu; };
2、基类和派生类对象赋值转换
- 派生类对象可以赋值给 基类的对象/基类的指针/基类的引用。我们可以称这个过程为切片或者切割
- 基类对象反之不能赋值给派生类对象
- 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象才是安全的。这里基类如果是多态类型,可以使用RTTI和dynamic_cast来进行识别后进行安全转换
class Person { protected:string _name;string _sex;int _age; };class Student : public Person { public:int _No; };void Test() {Student s1;//1.子类对象可以赋值给父类对象/指针/引用Person s2 = s1;Person* s3 = &s1;Person& s4 = s1;//2.基类对象不能赋值给派生类对象//s1 = s2//3.基类的指针可以通过强制类型转换赋值给派生类指针s3 = &s1;Student* ps1 = (Student*)s3;ps1->_No = 10;s3 = &s2; Student* ps2 = (Student*)s3;//这种可能会出现越界访问ps2->_No = 10; }
3、继承中的作用域
- 在继承体系中基类个派生类都有独立的作用域
- 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫做隐藏,也叫做重定义
- 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
- 注意在实际中的基础体系中最好不要定义同名的成员
class Person { protected:string _name = "haha";int _num = 11; };class Student : public Person { public:void Print(){cout << _name << endl;cout << Person::_num << endl;cout << _num << endl;} protected:int _num = 100; };void Test() {Student s1;s1.Print(); }int main() {Test();return 0; }class A { public:void fun(){cout << "fun()" << endl;} };class B : public A { public:void fun(int i){A::fun();cout << "fun(int i)" << i << endl;} };void Test() {B b;b.fun(10); }int main() {Test();return 0; }
4、派生类默认成员函数
在派生类中,成员函数是怎么生成的呢?
- 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类的构造函数的初始化列表阶段显示调用
- 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
- 派生类的operator=必须要调用基类的operator=完成基类的复制
- 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员在清理基类成员的顺序
- 派生类对象初始化先调用基类构造在调用派生类构造
- 派生类对象析构清理先调用派生类析构再调用基类的析构
- 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destructor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系
这个构造与析构顺序可以用栈来记忆
class Person { public:Person(const char* name = " peter"):_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){cout << "Student()" << endl;}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;}~Student(){cout << "~Student()" << endl;} protected:int _num; };void Test() {Student s1("jack", 10);Student s2(s1);Student s3("rose", 17);s1 = s3; }int main() {Test();return 0; }
5、继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有成员和保护成员
class Student; class Person { public:friend void Display(const Person& p, const Student& s); protected:string _name; };class Student : public Person { protected:int _stuid; };void Display(const Person& p, const Student& s) {cout << p._name << endl;cout << s._stuid << endl;//无法访问 }void Test() {Person p;Student s;Display(p, s); }
6、继承与静态成员
基类定义了static成员,则整个继承体系里面只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例
class Person { public:Person() { ++_count; } protected:string _name; public:static int _count; };int Person::_count = 0; class Student : public Person { protected:int _stuNum; };class Graduate : public Student { protected:string _semi; };void Test() {Student s1;Student s2;Student s3;Graduate s4;cout << "人数:" << Person::_count << endl;Student::_count = 0;cout << "人数:" << Person::_count << endl; }int main() {Test();return 0; }
7、复杂的菱形继承和菱形虚拟继承
单继承:一个子类只有一个直接相连的父类时称这个继承关系为单继承
多继承:一个子类有两个或两个以上直接相连的父类时称这个继承关系为多继承
菱形继承:菱形继承是多继承的一个特殊情况
菱形继承存在的问题:具有数据冗余和二义性问题,比如在最下面的Assistant对象中Person成员会有两份
class Person { public:string _name; };class Student : public Person { protected:int _num; };class Teacher : public Person { protected:int _id; };class Assistant : public Student, public Teacher { protected:string _majorcourse; };void Test() {Assistant a;//编译器会不知道访问哪个_namea._name = "peter";//只有指定访问哪个父类的成员可以解决歧义的问题a.Student::_name = "haha";a.Teacher::_name = "hehe"; }int main() {Test();return 0; }虚拟继承可以解决菱形继承的二义性和数据冗余的问题。在Student和Teacher的继承Person时使用虚拟继承,就可以解决问题了。并且,虚拟继承不要在其他地方使用
class Person { public:string _name; // 姓名 }; class Student : virtual public Person { protected:int _num; //学号 }; class Teacher : virtual public Person { protected:int _id; // 职工编号 }; class Assistant : public Student, public Teacher { protected:string _majorCourse; // 主修课程 }; void Test() {Assistant a;a._name = "peter"; }虚拟继承解决数据冗余和二义性问题的原理,我们用一下代码来帮助我们来学习
class A { public:int _a; }; // class B : public A class B : virtual public A { public:int _b; }; // class C : public A class C : virtual public A { public:int _c; }; class D : public B, public C { public:int _d; }; int main() {D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0; }编译器是这样子处理的:D对象中将A放到了对象呢组成的最下面,这个A同时属于B和C,那么A同时属于B和C,问题来了:B和C是如何找到公共的A呢?这里是通过了B和C的两个指针,指 向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量 可以找到下面的A。
相关文章:
C++学习第十二天(继承)
1、继承的概念以及定义 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行拓展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构&#x…...
WPF DataGrid绑定后端 在AutoGeneratingColumn事件中改变列名
public void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e){var propertyDescriptor (PropertyDescriptor)e.PropertyDescriptor;if (propertyDescriptor.IsBrowsable){e.Column.Header propertyDescriptor.DisplayName;}else{e.Cancel true;}}实体类中…...
2024 CorelDraw最新图形设计软件 激活安装教程来了
2024年3月,备受瞩目的矢量制图及设计软件——CorelDRAW Graphics Suite 2024 正式面向全球发布。这一重大更新不仅是 CorelDRAW 在 36 年创意服务历史中的又一重要里程碑,同时也展现了其在设计软件领域不断创新和卓越性能的领导地位。 链接: https://pan…...
双网口扩展IO支持8DO输出
M320E以太网远程I/O数据采集模块是一款工业级、隔离设计、高可靠性、高稳定性和高精度数据采集模块,嵌入式32位高性能微处理器MCU,集成2路工业10/100M自适应以太网模块里面。提供多种I/O,支持标准Modbus TCP,可集成到SCADA、OPC服…...
【负载均衡在线OJ项目日记】编译与日志功能开发
目录 日志功能开发 常见的日志等级 日志功能代码 编译功能开发 创建子进程和程序替换 重定向 编译功能代码 日志功能开发 日志在软件开发和运维中起着至关重要的作用,目前我们不谈运维只谈软件开发;日志最大的作用就是用于故障排查和调试&#x…...
yaml配置文件的在深度学习中的简单应用
1 .创作灵感 小伙伴们再阅读深度学习模型的代码的时候,经常会遇到yaml格式的配置文件。用这个配置文件是因为我们在训练模型的时候会涉及很多的参数,如果这些参数东一个,西一个,我们调起来的时候就会很不方便,所以用y…...
spring boot 核心配置文件是什么?
Spring Boot 的核心配置文件主要是 application.properties 或 application.yml(也称为 YAML 格式)。这两个文件通常位于项目的 src/main/resources 目录下,用于配置 Spring Boot 应用程序的各种属性和设置。 application.properties…...
Python的奇妙之旅——回顾其历史
我们这个神奇的宇宙里,有一个名叫Python的小家伙,它不仅聪明,而且充满活力。它一路走来,从一个小小的编程语言成长为如今全球最受欢迎的编程语言之一。今天,我们就来回顾一下Python的历史,看看它如何从一个…...
Flink面试整理-Flink的性能优化策略
Apache Flink 的性能优化是一个多方面的任务,涉及硬件资源、算法选择、配置调整等多个层面。以下是一些常见的 Flink 性能优化策略: 1. 资源分配和管理 合理配置 TaskManager 和 JobManager:根据作业的需求和可用资源,合理分配内存和 CPU 给 TaskManager 和 JobManager。适…...
SpringBoot与SpringMVC的区别
SpringBoot与SpringMVC的区别是什么? SpringBoot和SpringMVC是Java开发中常用的两个框架,它们都是由Spring框架所提供的,但在功能和使用方式上有着一些区别。本文将分别介绍SpringBoot和SpringMVC的特点和区别。 一、SpringBoot的特点&#…...
漏洞挖掘之某厂商OAuth2.0认证缺陷
0x00 前言 文章中的项目地址统一修改为: a.test.com 保护厂商也保护自己 0x01 OAuth2.0 经常出现的地方 1:网站登录处 2:社交帐号绑定处 0x02 某厂商绑定微博请求包 0x02.1 请求包1: Request: GET https://www.a.test.com/users/auth/weibo?…...
电脑屏幕监控软件都有哪些 | 五大好用屏幕监控软件盘点
电脑屏幕监控软件在企业管理、家庭教育等方面发挥着越来越重要的作用。 这些软件通过实时监控电脑屏幕活动,为用户提供了强大的管理和监控功能。 本文将为您盘点五大好用的电脑屏幕监控软件,帮助您更好地了解并选择适合自己的软件。 电脑屏幕监控软件都…...
数据结构-线性表-链表-2.3-2
在带头节点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一, 是编写算法实现上述操作。 双指针,用p从头至尾扫描单链表,pre指向*p结点的前驱,若p所指结点的值为x&#x…...
【自动化测试】使用MeterSphere进行接口测试
一、接口介绍二、接口测试的过程三、接口自动化测试执行自动化流程 四、接口之间的协议HTTP协议 五、 接口测试用例设计接口文档 六、使用MeterSphere创建接口测试创建接口定义设计接口测试用例 一、接口介绍 自动化测试按对象分为:单元测试、接口测试、UI测试等。…...
C语言 main( ) 函数的指针数组形参是怎么回事?
一、问题 在使⽤⼀些开发⼯具⽣成C语⾔⽂件时,主函数 mian( ) 中会有参数,这个参数到底是怎么回事⼉呢? 二、解答 mian( ) 称为主函数,是所有程序运⾏的⼊口。 mian( ) 函数是由系统调⽤的,当处于操作命令状态下&…...
汽车 - 什么是车轮抱死
车轮抱死分为两种情况,一种是车辆故障层面,另一种是驾驶过程中的物理现象。我们先来说最通俗的刹车车轮抱死吧。 刹车制动车轮抱死 车轮停止轴向转动就是抱死,有速度的情况下抱死车轮,如果车辆的惯性动能大于轮胎抓地力࿰…...
环保设备统一管理系统
在环保意识日益增强的今天,企业如何有效管理环保设备,确保其正常运行,减少环境污染,成为了一个重要议题。HiWoo Cloud平台以其独特的环保设备统一管理系统,为企业提供了一套完整的解决方案,帮助企业实现绿色…...
python 11Pandas数据可视化实验
实验目的: 学会使用Pandas操作数据集,并进行可视化。 数据集描述: 该数据集是CNKI中与“中药毒理反应”相关的文献信息,包含文章题目、作者、来源(出版社)、摘要、发表时间等信息。 实验要求࿱…...
【JUC】并发编程 AQS,ReentryLock,CyclicBarrier,CountDownLatch 原理总结
AQS AQS是什么?重写AQS就能实现锁的效果? AQS是一个抽象类,是一个并发包的基础组件,用来实现各种锁,同步组件的工具(通过volatile cas进行实现)。它包含了共享成员变量state、等待队列、条件…...
移动端底层事件(如左滑返回事件)在同一个路由下不同页面需要不同的处理要怎样才能做到统一处理?
目录 一、问题 二、解决方法 三、总结 tiips:如嫌繁琐,直接移步总结即可! 一、问题 1.测试提了个bug:进入了一个模块A里面的子页面 a1,左滑后按照用户预期应该是返回到模块A,结果回到了app首页。 二、解决方法 1.一开始:啊,…...
OpenClaw办公自动化:GLM-4.7-Flash处理Excel与PDF文档
OpenClaw办公自动化:GLM-4.7-Flash处理Excel与PDF文档 1. 为什么需要AI处理办公文档? 上周五下午5点,我正对着电脑屏幕发愁——市场部发来的20份PDF调研报告需要提取关键数据,财务部的季度Excel报表等着合并分析,而我…...
LTI系统设计避坑指南:因果性与稳定性在实际工程中的5个关键检查点
LTI系统设计避坑指南:因果性与稳定性在实际工程中的5个关键检查点 在数字信号处理领域,线性时不变(LTI)系统的设计是工程师日常工作的核心。然而,理论推导与工程实践之间往往存在一道鸿沟——许多在数学上完美的系统模…...
单一模型可能涌现不出超级智能,但 Agent 协作体却极有可能。
当 AI 把产品能力拉齐,注意力才是唯一的护城河 你有没有这种感觉?2025 年底,用 AI 一键生成一个完整 App 已经不是什么新闻,Vibe Coding 让普通开发者一天就能上线一个产品。可产品做出来了,下载量却像石沉大海&#x…...
效率飙升:借助快马平台生成全自动OpenClaw本地部署一体化工具
最近在折腾OpenClaw的本地部署时,发现传统方式实在太费时间了。每次都要手动查文档、拼命令、调环境,经常卡在某个依赖项版本冲突上。后来尝试用InsCode(快马)平台生成了一体化部署工具,效率直接翻倍。这里分享下具体实现思路和优化点&#x…...
nli-distilroberta-base模型服务监控:使用普罗米修斯与Grafana打造可视化看板
nli-distilroberta-base模型服务监控:使用普罗米修斯与Grafana打造可视化看板 1. 为什么需要模型服务监控 在生产环境中部署的AI模型服务,就像一台24小时运转的机器,需要随时掌握它的运行状态。想象一下,如果你不知道这台机器每…...
目标检测新手必看:如何用Python手写IOU计算函数(附完整代码)
目标检测实战:从零编写Python版IOU计算函数 刚接触目标检测时,最让人困惑的莫过于那些神秘的评估指标。其中IOU(交并比)就像一把尺子,能量化算法预测框与真实框的贴合程度。但纸上得来终觉浅,今天我们就用P…...
嵌入式通信协议SPI/I2C/UART原理与应用
嵌入式通信协议原理图解与技术解析1. 串行通信协议基础1.1 SPI通信协议SPI(Serial Peripheral Interface)是一种全双工、同步串行通信协议,采用主从架构设计。其核心特点包括:四线制结构:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、SS(片选…...
PHP 8.5 升级生存指南:避免凌晨两点回滚的检查清单
定目标版本,定义内部支持策略在动 CI 或 Composer 之前,先回答一个问题:在你的组织里,这次升级"完成"意味着什么?确定目标和截止日期PHP 分支有两年的活跃支持,然后是两年的安全修复。官方支持表…...
AtlasOS系统性能优化指南:从诊断到维护的全流程解决方案
AtlasOS系统性能优化指南:从诊断到维护的全流程解决方案 【免费下载链接】Atlas 🚀 An open and lightweight modification to Windows, designed to optimize performance, privacy and security. 项目地址: https://gitcode.com/GitHub_Trending/atl…...
RWKV7-1.5B-g1a轻量对话模型应用:微信公众号自动回复+知识库问答搭建
RWKV7-1.5B-g1a轻量对话模型应用:微信公众号自动回复知识库问答搭建 1. 模型简介与特点 rwkv7-1.5B-g1a 是基于 RWKV-7 架构的多语言文本生成模型,特别适合中文轻量对话场景。相比传统大模型,它具有以下优势: 资源占用低&#…...




