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

C++之继承详解

一.继承基础知识

继承定义:

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

 

例如:

class Person
{
public://
private:int _age;string cellphone;string name;
};
class Student :public Person
{
public://
private:int _student_id;
};
class Teacher :public Person
{
public://
private:int _teacher_id;
};

你仔细观察上面代码,会发现再学生和老师类上我们后面都跟了:public Person说明这两个类是继承Person的内容

现在我们再来学习下格式:

定义格式:

关于派生类和基类大家看下都能理解,现在我们重点来讲解下什么是继承方式

继承方式简单理解就是子类对于父类中的成员可以进行的操作,如下:

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected
成员
派生类的private
成员
基类的protected
成员
派生类的protected
成员
派生类的protected
成员
派生类的private
成员
基类的private成
在派生类中不可见在派生类中不可见在派生类中不可 见

总结:
1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私
有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面
都不能去访问它

2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在
派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他
成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected
> private。

4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过
最好显示的写出继承方式。
5. 在实际运用中一般使用都是public继承,几乎

很少使用protetced/private继承,也不提倡
使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里
面使用,实际中扩展维护性不强

 

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

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割
。寓意把派生类中父类那部分切来赋值过去。
基类对象不能赋值给派生类对象。
基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换

 

如下:

class Person
{
public:
private:int age;string name;
};
class Student :public Person
{
public:
private:string _id;
};
class Teacher :public Person
{
public:
private:string _id;
};
int main()
{Student s;//子类可以切片给父类对象//派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用//派生类对象 可以赋值给 基类的对象Person p1 = s;//派生类对象 可以赋值给 基类的指针Person* p2 = &s;//派生类对象 可以赋值给 基类的引用Person& p3 = s;//但注意;基类不能赋值给派生类Person p4;//Teacher t1 = p4;return 0;
}

但基类一定不能传给派生类吗???

我们发现此时是可以赋值给子类了,这是特殊情况的处理,而且转换时可能会出现越界的情况

知识扩展:

我们知道在不同类型赋值时会存在类型转换,那么你知道下面这行代码出错的原因吗???

看到报错原因,你以为是int不能传给double,还是要强制类型转换

实际上这是因为引用赋值时,会产生一个临时变量,该零时变量具备常性,所以我们必须用const修饰,防止权限扩大

这样就可以通过了

总结:如果要通过引用来复制拷贝,我们一定要观察是否会出现零时变量,从而确定是否需要用const修饰

三.继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域
2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
也叫重定义
。(在子类成员函数中,可以使用 基类::基类成员 显示访问
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
4. 注意在实际中在继承体系里面最好不要定义同名的成员

 

如果你仔细观察过上一个代码,你会发现在Student和Teacher中都出现了_id这个成员变量,这就是隐藏

下面在看这道题:

class A
{
public:void func(){//}
private:int _a;
};
class B :public A
{
public:void func(int a=10){//}
private:int _a;
};

请问B中的func函数和A中的func函数是什么关系???

a.   无关系                         b.  重定义(隐藏)                           c.重载

答案是:b

c重载是指在相同作用域中两个同名函数由于参数的不同所构成的,而且只有返回类型不同是不构成重载的

而且这两个类属于不同作用域,看定义:
成员函数的隐藏,只需要函数名相同就构成隐藏

那么我们如何访问父类成员变量呢???

如下:

四.派生类的默认成员函数(重点)

class Person
{
public://构造函数;Person(const string& s):_name(s){}//拷贝构造Person(const Person& p):_name(p._name){}//复值重载Person& operator=(const Person& p){if (&p != this){this->_name = p._name;return *this;}else{exit(-1);}}//析构函数~Person(){//析构}
protected:string _name;
};
class Student :public Person
{
public://构造函数;//派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认//的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用Student(const Student& s):_age(s._age),Person(s){}Student(const int& age,const string& name):_age(age),Person(name){}//注意的是:构造函数实际给成员变量赋值时,是先给父类赋值的,即使我们初始化列表中子类在前!!!//拷贝构造//派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化Student(const Student& s):_age(s._age),Person(s)//注意这里是切割{}//赋值重载//派生类的operator = 必须要调用基类的operator = 完成基类的复制Student& operator=(const Student& s){if(this!=&s){_age = s._age;Person::operator=(s);//注意这里operator函数和父类是隐藏的关系}else{exit(-1);}}//析构函数://派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能//保证派生类对象先清理派生类成员再清理基类成员的顺序//析构先析构子类,构造先构造父类~Student(){//析构子类//父类会自动调用的,不用写}
protected:int _age;
};

五.友元和静态成员

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
 

此时我们写成友元就可以访问了

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子
类,都只有一个static成员实例
即我们可以通过父类和子类访问同一个静态成员

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

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

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

此时就会出现一种特殊的继承;菱形继承:菱形继承是多继承的一种特殊情况

菱形继承的问题:可以看出菱形继承有数据冗余和二义性的问题。在d的对象中A成员会有两份

此时我们通过限定符去访问A中的成员对象就会出现B和C都可以访问,而且访问的是不同的两个成员,此时就会出现二义性和冗杂
 

此时,C++引入了虚继承的概念。

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在B和C的继承A时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
 

class Person
{
public:
protected: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; // 主修课程
};

如图,虚继承只需要在:后面写上一个virtual即可,还要注意的是,不同的父类要通过,分隔开

接下来我们深入底层来看看虚继承是如何解决问题的。

菱形继承例子:

这就是一个典型的菱形继承关系

最后,感谢大家的支持!!!

相关文章:

C++之继承详解

一.继承基础知识 继承定义: 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保 持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象 程序设…...

C#装箱和拆箱

一,装箱 装箱是指将值类型转化为引用类型。 代码如下: 装箱的内部过程 当值类型需要被装箱为引用类型时,CLR(Common Language Runtime)会为值类型分配内存,在堆上创建一个新的对象。值类型的数据会被复…...

企业用大模型如何更具「效价比」?百度智能云发布5款大模型新品

服务8万企业用户,累计帮助用户精调1.3万个大模型,帮助用户开发出16万个大模型应用,自2023年12月以来百度智能云千帆大模型平台API日调用量环比增长97%...从一年前国内大模型平台的“开路先锋”到如今的大模型“超级工厂”,百度智能…...

linux 外部GPIO Watchdog驱动适配

前言 文章描述, 利用外部gpio看门狗芯片驱动芯片的复位功能。 芯片:RK3568 平台: Linux ubuntu.lan 4.19.232 #27 SMP Sat Sep 23 13:43:49 CST 2023 aarch64 aarch64 aarch64 GNU/Linux 硬件接线图示 看门狗芯片采用GPIO喂狗,W…...

活动回顾 | 走进华为向深问路,交流数智办公新体验

3月20日下午,“企业数智办公之走进华为”交流活动在华为上海研究所成功举办。此次活动由上海恒驰信息系统有限公司主办,华为云计算技术有限公司和上海利唐信息科技有限公司协办,旨在通过对企业数字差旅和HR数智化解决方案的交流,探…...

【Java】Oracle发布Java22最新版本

甲骨文(ORACLE)已经于2023年3月19日正式发布了最新版本的JDK,版本号:22 根据官方声明,Java 22 (Oracle JDK 22) 在性能、稳定性和安全性方面进行了数千种改进,包括对Java 语言、其API 和性能,以…...

Vue reactive函数的使用

let searchForm reactive({}); let data reactive({ isAdmin: true, isshowAccount: true }); reactive 是什么? reactive 是 Vue 3 Composition API 中的一个函数,用于创建一个包含响应式数据的对象。在 Vue 2.x 中,我们通常使用 data 选项…...

unity自动引用生成

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using UnityEditor; using UnityEngine; using UnityEngine.UI;/// <summary> /// 模板脚本生成 /// </summary> public class ScriptCreater : EditorW…...

【Linux系统】线程互斥与同步

目录 一.几个概念 二.线程互斥 1.定义并初始化锁 2.加锁 3.解锁 4.销毁锁 三.互斥锁的本质 1.xchg的原子性 2.加锁的过程 3.解锁的过程 四.可重入VS线程安全 五.死锁 1.死锁的概念 2.具体实例 3.死锁产生的四个必要条件 4.解决或避免死锁 六.线程同步 七.…...

武汉星起航引领跨境电商新潮流,深耕亚马逊打造全方位合作新模式

在全球化的浪潮下&#xff0c;跨境电商已成为连接各国市场的重要桥梁&#xff0c;为无数企业带来了前所未有的发展机遇。在这一领域&#xff0c;武汉星起航电子商务有限公司以其独特的战略眼光和实战经验&#xff0c;成为引领行业发展的佼佼者。公司自2017年起便深耕亚马逊平台…...

GateWay路由规则

Spring Cloud GateWay 帮我们内置了很多 Predicates功能&#xff0c;实现了各种路由匹配规 则&#xff08;通过 Header、请求参数等作为条件&#xff09;匹配到对应的路由 1 时间点后匹配 server:port: 8888 spring:application:name: gateway-servicecloud:nacos:discovery:…...

shell脚本基础改造

一、基础的shell脚本格式 #!/bin/bash 2 #3 #********************************************************************4 #Author: LJH5 #QQ: 2…...

静态综合实验

一&#xff0c;1.搭建拓扑结构并启动。 2.根据题意得该图需要14个网段&#xff0c;根据192.168.1.0/24划分子网段&#xff0c;如下&#xff1a; 划分完如图所示&#xff1a; 二、配置IP地址 R1路由器&#xff1a; 1.进入系统视图并改名. 2.接口配置IP地址&#xff1a…...

Spring Web MVC入门(6)

应用分层 在开发的过程中, 我们会发现, 程序的代码有时会很"杂乱", 如果后面的项目更大了, 那就会更加地杂乱无章(文件乱, 代码内容乱). 也基于此, 接下来让我们来学习一下应用分层. 也类似于公司的组织架构 公司初创阶段, 一个人身兼数职, 既做财务, 又做人事,还有…...

muduo异步日志

muduo异步日志实现 陈硕老师的muduo网络库的异步日志的实现&#xff0c;今晚有点晚了&#xff0c;我明晚再把这个异步日志抽出来&#xff0c;作为一个独立的日志库。 所在文件 AsyncLogging.cc AsyncLogging.h LogFile.h LogFile.cc CountDownLatch.h CountDownLatch.cc…...

在智慧能源的发展历程中,哪些技术的出现起到了关键性的作用?

智慧能源作为一种全新的能源发展理念&#xff0c;正逐渐成为能源领域的热门话题。在智慧能源的发展历程中&#xff0c;有许多技术的出现起到了关键性的作用&#xff0c;推动了智慧能源的快速发展。 一、物联网技术 物联网技术使得能源设备可以实现互联互通&#xff0c;通过传感…...

SQLiteC/C++接口详细介绍sqlite3_stmt类(十三)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十二&#xff09; 下一篇&#xff1a; 待续 51、sqlite3_stmt_scanstatus_reset sqlite3_stmt_scanstatus_reset 函数用于重置指定语句对象最近一次执行的 WHER…...

扫雷(蓝桥杯,acwing)

题目描述&#xff1a; 扫雷是一种计算机游戏&#xff0c;在 2020 世纪 80 年代开始流行&#xff0c;并且仍然包含在某些版本的 Microsoft Windows 操作系统中。 在这个问题中&#xff0c;你正在一个矩形网格上玩扫雷游戏。 最初网格内的所有单元格都呈未打开状态。 其中 M个…...

macOS 通过 MacPorts 正确安装 MySQL 同时解决无法连接问题

如果你通过 sudo port install 命令正常安装了 MySQL&#xff0c;再通过 sudo port load 命令启动了 MySQL Server&#xff0c;此刻却发现使用 Navicat 之类的 GUI 软件无法连接&#xff0c;始终返回无法连接到 127.0.0.1 服务器。这是一个小坑&#xff0c;因为他默认使用了 So…...

Semi-supervised Open-World Object Detection

Semi-supervised Open-World Object Detection 摘要1 介绍2.准备工作提出的SS-OWOD问题设置2.1 基础架构3 方法3.1整体架构摘要 传统的开放世界对象检测(OWOD)问题设置首先区分已知和未知类别,然后在后续任务中引入标签时逐步学习未知对象。然而,当前的OWOD公式在增量学习…...

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

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

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...