当前位置: 首页 > news >正文

C++ 继承【一篇让你学会继承】

1. 继承的概念及定义

1.1 继承的概念

继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特征的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,填了由简单到复杂的认知过程。继承是类设计层次的复用。

1.2 继承定义

1.2.1 定义格式

1.2.2 继承关系和访问限定符

1.2.3 继承基类成员访问方式的变化

总结:

1.基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类内还是类外都不能去访问它。除非使用父类中公有的成员函数,通过这个公有的成员函数,间接的访问父类中的私有成员。

(抽象:你继承了你父亲的产业,但是你父亲的私房钱,你是不可以继承使用的。你父亲的私房钱是实际存在的,如果真的想用父亲的私房钱,就需要通过间接的方式,比如让你父亲请客,这样就间接的使用到父亲的私房钱。)

通过上图的调试,可以看出基类private成员还是被派生类对象继承了,我们通过了公有的Print成员函数,访问到了父类中的“_name”和“_age”。

如果我们不通过公有成员函数访问父类中的私有,就会报错(如下图)

如果基类成员不想在类外直接被访问,但需要在派生类中访问,那就将private改成protected。(如下图)

 2.通过上面的表格,我们可以发现,基类的其他成员在子类的访问方式==Min(成员在基类的访问限定符,继承方式),public>protected>private。

比如:

但是protected的继承方式,子类中是可以调用Public成员函数的。(如下图)

再比如,子类通过私有方式继承父类,子类继承父类中所有的成员(包含公有成员、保护成员、私有成员(私有成员不能直接访问)),但这些成员在子类中变成了私有。

3.使用关键字class时默认的继承方式是private,使用strcut时默认的继承方式是public,不过最好显示的写出继承方式。

5.在实际运用中一班使用都是public继承,几乎很少使用protected/private继承,也不提倡使用protected/private继承,因为protected/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

2. 基类和派生类对象赋值转换

派生类对象可以赋值给基类的对象/基类的指针/基类的引用。这里有个形象的说法叫切片或者切割。指把派生类中父亲的那部分切来赋值过去。

我们先来看一个其他东西。(如下图)

上图,b在给a当别名的时候,因为类型不匹配,产生的临时变量是常量,导致权限放大,所以加上了const。这是类型转换。

而我们的赋值转换,在引用的时候,并没有加上const(如下图)。因为他们两个不是一个东西,赋值转换是一个特殊语法规则。注意区分!

赋值兼容转换主要适用于公有继承的情形。在私有或保护继承的情况下,这种转换通常会导致编译错误或者运行时错误。因为私有继承和保护继承改变了基类成员的访问性,基类的接口与派生类的实际内部结构不匹配。

③基类对象不能赋值给派生类对象

④基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI的dynamic cast来进行识别后进行安全转换。(以后的文章会涉及,这边初步了解)

通过下图,我们可以直观看见子类对象可以赋值给父类对象/指针/引用,并且父类对象指向/引用子类中父类的部分。

3. 继承中的作用域

1.在继承体系中基类和派生类都有独立的作用域。

2. 子类和父类中有同名成员,子类成员将屏蔽父亲对同名成员的直接访问,这种情况叫隐藏,也叫重定义。

这个不是重载,重载是在同一作用域下,而父类和子类是两个独立的作用域,出现同名,是隐藏。

(在子类成员函数中,可以使用基类::基类成员 显示访问)

正如下图中,父类有Public,则调用父类的

如果子类有,则调用Public

所以,查找规则就是就近原则有子类用子类,没有子类用父类。

如果有子类的情况下,非要调用父类的话,可以使用基类::基类成员 显示访问(如下图)

3.需要注意的是如果成员函数的隐藏,只需要函数名相同就构成隐藏。

即使参数不一样

4.注意在实际中在继承体系里面最好不要定义同名成员。

4.派生类的默认成员函数

默认构造函数:①我们没有写编译器自己生成的 ②全缺省的 ③不需要传参的

1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。(可以把父类当做一个整体,相当于是子类中的一个自定义成员对象)

如果子类调用默认构造,但是父类中没有默认构造,就会报错

修改方式如下图,在子类中显示调用父类的构造函数

要不然就是父类中也有一个默认构造函数

2.拷贝构造函数

如果不需要深拷贝,可以不用我们自己写,使用编译器生成的默认拷贝构造完成拷贝初始化

如果在子类中需要深拷贝,则在子类中手动完成拷贝构造,并及时传父类部分,完成父类的拷贝

 如果父类中需要深拷贝,直接使用子类传来对应父类的值,再完成深拷贝

3.operator=

如果不需要赋值深拷贝,直接使用编译器自动生成的默认赋值

如果需要子类中需要赋值深拷贝,则在子类中手动完成operator=,并及时传父类部分,完成父类部分的赋值(如下图)

如果父类中需要赋值深拷贝,直接使用子类传来对应父类的值,再完成赋值深拷贝

 4.完成构造的时候都是先父后子初始化列表初始化顺序是按照声明的顺序初始化的,而父类是最先声明的,所以在子类初始化列表中,无论是否显示切片传值给父类,都是先初始化父类。所以构造是可以保证先父后子的。

5.完成析构函数是先子后父。如果顺序相反,则可能导致资源管理上的问题,特别是当基类和派生类都包含需要清理的资源时。

为了保证先子后父,在完成子类的析构函数后,自动调用父类的的析构函数清理基类成员。

如果遇到这种情况呢(如下图),下图直接就只调用了父类的析构,导致子类中资源没有被清理。

表面上析构是~+类名,其实本质上析构函数都是同一个名字destructor,所以这边父类和子类的析构就构成了隐藏的关系。这边ptr是父类对象,在析构时就调用了父类的析构函数。

那怎么样我们才能调用到子类的析构函数呢?(如下图)

正如上图所示,在父类的析构函数上面加一个virtual。ptr是父类对象,但是指向的是子类,加上virtual,形成多态,就可以指向谁就调用谁的函数。这样这边就调用了子类的析构,再自动调用父类的析构函数。(关于多态,将会在本篇文章的下一篇文章中呈现)

5. 继承与友元

友元关系不能被继承

也就是说基类友元不能访问子类私有和保护成员。

除非基类友元也是子类的友元

可以这样抽象理解,父亲的朋友不是你的朋友(基类友元不能访问子类私有),除非你和你父亲的朋友成为朋友(基类友元也成为子类的友元)。

6. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。

无论派生出多少个子类,都只有一个static成员实例。

7. 复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

多继承:一个子类有两个或者以上直接父类时称这个继承关系为多继承

菱形继承:菱形继承是多继承的一种特殊情况

 菱形继承的问题:菱形继承有数据冗余(数据重复)和二义性(两种意思)的问题。

上图可以看出在Assistant中,他的两个父类Student和Teacher都包含Person,所以Assistant中有两个Person。导致访问_name的时候,不知道访问的是Student中的_name,还是Teacher中的_name。

在下图中,通过指定作用域的方式,解决了数据二义性的问题。

怎么办呢?既可以解决二义性问题,又可以解决数据冗余问题。

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。

正如下图中,在Student和Teacher继承时加上了virtual了之后,这样在Assistant的对象a中Person的内容就只保留了一份。

8. 继承的总结和反思

1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。

2. 多继承可以认为是C++缺陷之一,很多后来的语言都没有多继承,如Java。

3. 继承和组合

  • public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
  • 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
  • 优先使用对象组合,而不是继承。
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
  • 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

 好了,本篇文章就到这里~

欢迎指正批评~

相关文章:

C++ 继承【一篇让你学会继承】

1. 继承的概念及定义 1.1 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特征的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构&…...

DeviceNet网关HT3S-DNS-MDN读取七星华创CS310空气流量计数据应用案例

七星华创流量计CS310系列 (MODBUS RTU) 通过DeviceNet网关HT3S-DNS-MDN 与台达DVP系列的PLC进行交换数据应用案例 一、概述 本文主要介绍使用HI-TOP网关 HT3S-DNS-MDN在台达DVP系列 PLC和七星华创CS310流量计之间进行数据交换。 解决的问题:台达DVP系列如何通过…...

Smartbi体验中心新增系列Demo,用户体验更丰富

为进一步提升用户体验,让大家更直观地了解Smartbi产品在数据分析方面的功能优势,Smartbi体验中心近期新增了一系列Demo。这些更新旨在优化产品操作流程,并为用户提供更多真实场景下的应用参考。接下来,我们一起简要浏览此次体验中…...

Kubernetes 与 springboot集成

Kubernetes 与 Spring Boot 集成详解 Kubernetes(简称 K8s)是一个用于自动化部署、扩展和管理容器化应用的开源平台,而 Spring Boot 是 Java 开发领域中非常流行的微服务框架。将这两者结合,可以充分利用 Kubernetes 强大的容器编…...

以太网传输出现不分包

最近对手件反馈,在传输文件的时候,我们这边发包太快,导致对手件网络出现了拥塞,把他们程序给搞死了。他们抓了一下他们收到的包,发现我们发送的数据包都大于了MTU设置的值。现在被要求更改。 排查方法:为什么我们发送的数据包会大于MTU的值。 可能性一:配置了Dont Fra…...

[实践应用] 深度学习之激活函数

文章总览:YuanDaiMa2048博客文章总览 深度学习之激活函数 激活函数基本概念分类常见的激活函数2. Tanh/双曲正切激活函数3. ReLU激活函数4. Softmax激活函数 PyTorch中如何使用1. 线性激活函数2. 非线性激活函数SigmoidTanhReLULeaky ReLUParametric ReLU (PReLU) 使…...

Java基础之数组

文章地址:Java基础之数组 码农爱刷题 为计算机编程爱好者和从业人士提供技术总结和分享 !为前行者蓄力,为后来者探路!...

基于SpringBoot+Vue的智慧自习室预约管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…...

pptpd配置文件/etc/pptpd.conf详解

正文共:1111 字 2 图,预估阅读时间:1 分钟 如果要在Linux系统配置PPTP(Point-to-Point Tunneling Protocol,点到点隧道协议)VPN,一般是使用pptpd软件。pptpd命令通常从配置文件/etc/pptpd.conf中…...

springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件

首先,对数据库进行备份,用到的命令: mysqldump --opt -h 192.168.1.200 --userroot --passwordxxx --result-fileE://data//20240911141400.sql --default-character-setutf8 xxx(数据库名) 直接上代码 配置文件部分…...

【软考中级攻略站】-软件设计师(4)-计算机网络基础

计算机网络的分类 1. 局域网(Local Area Network, LAN) 局域网是指在一个较小的地理区域内连接的计算机网络,比如学校的机房、公司的办公室或者家里的Wi-Fi网络。在这个网络内的计算机可以直接相互通信,速度很快,延迟…...

Android以及IoT设备传感器软件开发总结

1 传感器选型 1.1 传感器选型 6 axis:Bosch BMI160(比较差),InvenSense MPU6050(DMP),ST LSM6Dxx Acc: Freescale MMA7450L (MicroMachined Accelerometer/MC Multi-Axis, iMX31), Kionix KXSD9…...

Vue2/Vue3中编程式路由导航实践总结

【1】Vue2编程式路由导航 ① router.push 除了使用 <router-link> 创建 a 标签来定义导航链接&#xff0c;我们还可以借助 router 的实例方法&#xff0c;通过编写代码来实现。 router.push(location, onComplete?, onAbort?)注意&#xff1a;在 Vue 实例内部&#…...

【nginx】ngx_http_proxy_connect_module 正向代理

50.65无法访问 服务器, (403 错误) 50.196 可以访问服务器。 那么,配置65 通过196 访问。 需要一个nginx作为代理 【nginx】搭配okhttp 配置反向代理 发送原生的nginx是不支持okhttp的CONNECT请求的。 大神竟然给出了一个java工程 GINX编译ngx_http_proxy_connect_module及做…...

单考一个OCP认证?还是OCP和OCM认证都要考?

​ Oracle的OCP认证是数据库行业非常经典的一个认证&#xff0c;从事数据库行业的人都建考一个 Oracle OCP 认证。 OCP认证内容包括&#xff1a; OCA部分&#xff1a;数据库基础知识、SQL 语言使用、基本的数据库管理技能等&#xff0c;如数据库安装与配置、理解数据库架构、…...

在Spring官网查看Springboot与Java的版本对应关系

查看Spring Boot与Java的版本对应关系&#xff0c;可以按照以下步骤操作&#xff1a; 访问Spring官方网站&#xff0c;进入Spring Boot项目页面。可以通过点击菜单中的“Projects”&#xff0c;然后选择“Spring Boot”来访问。Spring | Home 在Spring Boot的LEARN页签中&…...

HarmonyOS学习(十二)——数据管理(一)分布式数据

文章目录 1、分布式数据服务概述2、KV数据模型&#xff08;键值对数据库&#xff09;3、分布式数据服务的约束和限制4、接口说明5、分布式数据服务开发步骤5.1、导入模块5.2、构造分布式数据库管理类实例5.3、获取、创建分布式数据库5.4、订阅分布式数据库的数据变化5.5、插入数…...

3D GS 测试自己的数据

环境配置 win11 vs2019cuda11.8driver522.06python3.10pytorch 2.4.0colmap3.8&#xff08;可选&#xff0c;用于将图像生成点云&#xff09; 安装 1 minicodagit 略 2 vs2019 在装cuda前安装&#xff0c; 选择c桌面开发即可&#xff0c; 环境变量path中配置C:\Program…...

攻防世界 supersqli

supersqli 一般sql语句的题都是先判断&#xff0c;经过测试&#xff0c;是单引号注入 999 union select database(),2#可以发现很多关键字都被过滤了select&#xff0c;所以联合查询&#xff0c;报错注入&#xff0c;布尔和时间盲注都不能用了&#xff0c;可以想到堆叠注入。…...

OceanBase 运维管理工具 OCP 4.x 升级:聚焦高可用、易用性及可观测性

可视化的管控平台&#xff0c;对 OceanBase 这类的分布式数据库及大规模数据的运维管理来说&#xff0c;是提升运维效率与数据库管理水平的重要工具。OceanBase 运维管理工具 OCP 作为专为OceanBase数据库设计的企业级全生命周期管理平台&#xff0c;为用户提供了全面的数据库可…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...