C++ - 继承
继承的基本概念
继承就是一种代码的复用.
子类通过继承父类, 就能使用父类的变量, 方法.
学生和老师这两种身份, 他们都有共同的属性: 他们都有名称, 年龄, 性别 ....
当然他们也有各种独有的属性, 学生有学号, 老师有工号 ....
对于这些共有的属性, 我们可以将它们提取出来:
class person
{public:string name;string sex;int age;
};
然后让学生类和老师类去继承这个 person 类.
class student : public person
{
public:string student_ID; // 学号
};
class teacher : public person
{
public:string job_ID;
};
class student : public person
student: 派生类 (又称为子类)
public: 继承方式
person: 基类 (又称为父类)
现在实例化出一个 student 对象之后, 这个对象也有 person 中的属性.
student st;
st.name = "张三";
st.age = 14;
st.student_ID = "123456";
继承关系和访问限定符
C++ 中存在三种访问限定符
- private: 只能在类内访问
- protected: 允许类内和派生类 (子类) 访问
- public: 无论类内类外都可以访问
那么当这三种访问限定符用在继承中, 有什么样的效果.
子类继承父类时, 会继承父类的成员属性和成员方法.
继承存在自己的继承关系 (private ...), 属性和方法也有自己的访问修饰限定符.
这两者之间的关系是如何的呢?
结论: 取 继承关系的限定符 和 访问修饰限定符中, 权限小的那一个
class person
{
public:int a;
protected:int b;
public:int c;
};// 1. public 继承
class student1 : public person
{//父类所有的属性都会被继承
public:int a;
protected: // protect 和 public 取权限小的那一个int b;
};// 2. private 继承
class student2 : private person
{
private: // private 和 public 取权限小的那一个int a;
private:int b;
};
我们可以看到, 继承得到的属性和方法的访问限定符, 是取二者之间较小的那一个.
但是在上面的两个继承的 student 类中, 我没有将变量 c 写出来, 这是为什么?
很简单, 变量 c 由 private 修饰, 所以只能在 person 中访问, 即便 student 继承了 person,
student 是独立的, 不是 person 的一部分, 也就无法访问 c.
一般而言, 都是使用 public 继承.
这里用一张图来表明继承后的方法和属性的访问权限
继承的作用域
从上面的成员的继承, 我们能察觉到, 派生类和基类是两个不同的空间.
那么既然是不同的空间, 在派生类中, 就可以定义和父类一样的成员属性和成员方法.
class person
{
public:int add(int a, int b){}string name;int age;
};class student : public person
{
public:int add(){return person::add(10, 20);}string name;
};student st;
st.name; // 就近原则, 默认访问子类的
st.person::name; // 带上基类的作用域, 才会访问到基类的属性
在同一个作用域中, 是不能存在两个同名变量的, 但是派生类和基类是不同的作用域. 他们的作用域都是独立的.
所以, 在派生类和基类中定义名称相同的变量也是可以的. (但是在实际使用中, 是不推荐这样写的)
那么这里就有一个很容易犯错的点. 上面代码中的 add 函数, 是不是构成了重载?
当然不是, 重载的条件之一就是: 同名函数处于同一作用域, 派生类和基类属于不同作用域.
当然也就不构成重载.
事实上, 这种情况称为隐藏 (也成为重定义). 在子类成员函数中,可以使用 基类::基类成员 访问.
父子类的对象赋值转换
- 子类对象赋值给基类的 对象/基类指针/基类引用
- 父类对象不能赋值给派生类对象
因为子类拥有父类的所有属性和方法, 父类赋值给子类, 就是把子类中, 父类的那部分给切出来.
这个过程称为"切片".
子类中的默认成员函数
- 子类的构造/拷贝构造/赋值重载函数, 都需要去调用父类的构造/拷贝构造/赋值重载.
- 子类的析构函数, 会自动的调用父类的析构函数, 所以不用显示的写出来
- 当实例化一个子类对象时, 会先初始化父类的成员变量, 然后再初始化子类的成员变量
- 当析构一个子类对象时, 会先调用子类的析构函数, 然后再调用父类的析构函数. (与构造函数调用顺序相反)
下面代码可以通过打印顺序观察各自的调用顺序.
class person
{
public:person(const char* name = "parent"):_name(name){cout << "person()" << endl;}person(const person& p){_name = p._name;cout << "person(const person&)" << endl;}person& operator= (const person& p){cout << "person operator=" << endl;if(this != &p){_name = p._name;}return *this;}~person(){cout << "~person()" << endl;}
private:string _name
};class student : public person
{
public:student(const char* name, int age):person(name),_age(age){cout << "student()" << endl;}student(const student& st):person(s),_age(st._age){}student& operator= (const student& st){cout << "student operator=" << endl;person::operator=(st);_age = st._age;return *this;}~student(){cout << "~student" << endl;}
private:int _age;
};int main()
{student s1("zhangsan", 10);student s2(s1);student s3("lisi", 20);s1 = s3;return 0;
}
菱形继承和虚继承
在C++中, 允许一个派生类继承多个基类, 这样会导致一个问题.
那么此时问题来了, D同时继承了 B和C, B和C 都继承了A.
那么D中就存在两份类A的成员属性, 一份来自B, 一份来自C.
此时D中就多余了一份数据, 占用了额外的空间.
更重要的是, 造成了二义性. 当我们访问继承至类A的属性时,
访问的是通过B继承得来的, 还是通过继承C得来的. 无法区分.
所以这里就引出了一个方法来解决这个菱形继承的问题.
虚继承: 在 B, C 继承前加上 virtual 关键字.
class A
{int a;
};class B : virtual public A
{int b;
};class C : virtual public A
{int c;
};class D : public B, A
{int d;
};
virtual 关键字要加在 B, C前, 而不是 D 前.
在实际写代码中, 是很少会使用多继承的, 这样带来的不可控影响太大了.
所以在实际写代码中, 能不用多继承就不用多继承. 这部分了解即可
相关文章:

C++ - 继承
继承的基本概念 继承就是一种代码的复用. 子类通过继承父类, 就能使用父类的变量, 方法. 学生和老师这两种身份, 他们都有共同的属性: 他们都有名称, 年龄, 性别 .... 当然他们也有各种独有的属性, 学生有学号, 老师有工号 .... 对于这些共有的属性, 我们可以将它们提取出来: …...

华为服务器使用U盘重装系统
一、准备工作 下载官方系统(注意服务器CPU的架构是x86-64还是aarch64,不然可能报意想不到的错)制作启动U盘(下载rufus制作工具,注意文件系统选FAT32还是NTFS) 二、安装步骤 将U盘插入USB接口重启服务器…...

网络分层模型( OSI、TCP/IP、五层协议)
1、网络分层模型 计算机网络是一个极其复杂的系统。想象一下最简单的情况:两台连接在网络上的计算机需要相互传输文件。不仅需要确保存在一条传输数据的通路,还需要完成以下几项工作: 发起通信的计算机必须激活数据通路,这包括发…...

前端开发 之 15个页面加载特效上【附完整源码】
文章目录 一:彩球环绕加载特效1.效果展示2.HTML完整代码 二:跷跷板加载特效1.效果展示2.HTML完整代码 三:两个圆形加载特效1.效果展示2.HTML完整代码 四:半环加载特效1.效果展示2.HTML完整代码 五:音乐波动加载特效1.效…...
Spring Boot使用JDK 21虚拟线程
JDK 21引入的虚拟线程(Virtual Threads)是 Project Loom 的一部分,旨在显著简化并发编程并提高 Java 应用的可扩展性。以下是虚拟线程的主要特点: 1. 概念 虚拟线程是轻量级线程,与传统的操作系统线程不同࿰…...

《从0到1常用Map集合核心摘要 + 不深不浅底层核心》
《从0到1常用Map集合核心摘要不深不浅底层核心》 前置知识 什么是键值对 键值对是一种数据结构,键是唯一标识,值是对应数据,用来快速查找信息。例: {"name": "Alice"},键是name,…...
12 设计模式之工厂方法模式
一、什么是工厂方法模式? 1.定义 在软件开发中,设计模式 是解决常见软件设计问题的最佳实践。而 工厂方法模式(Factory Method Pattern) 作为创建型设计模式之一,常常被用来解决对象创建问题。它通过将对象的创建交给…...
spaCy 入门与实战:强大的自然语言处理库
spaCy 入门与实战:强大的自然语言处理库 spaCy 是一个现代化、工业级的自然语言处理(NLP)库,以高效、易用和功能丰富著称。它被广泛应用于文本处理、信息提取和机器学习任务中。本文将介绍 spaCy 的核心功能,并通过一…...
python包的管理和安装——笔记
1.列出包 pip list pip freeze 用这2个可以查看当前python 下所有的包和版本,还有下载地址 如果只是想导出当前的环境 可以用 2.安装pipreqs pip install pipreqs,pipreqs ./可以导出当前项目的包这个包 遇到编码报错 pipreqs ./ --encodingutf8 p…...

Vue前端页面内嵌套本项目iframe窗口的通信传输方式
一、目的 想要在iframe中使用本项目页面、并能够与其父页面组件实现实时通信。Vue前端页面内嵌套本项目iframe窗口的通信传输方式-星林社区 https://www.jl1mall.com/forum/PostDetail?postId20241202172800023969 二、iframe通信方式 1.接收消息 页面需要监听 message 事件…...
【WEB开发.js】addEventListener事件监听器的绑定和执行次数的问题(小心踩坑)
假设我们有一个按钮,用户点击该按钮后,会选择一个文件,且我们希望每次点击按钮时只触发一次文件处理。下面我会给你一个简单的例子,展示放在函数内部和放在函数外部的区别。 1. 将事件监听器放在函数内部(问题的根源&…...

用于LiDAR测量的1.58um单芯片MOPA(一)
--翻译自M. Faugeron、M. Krakowski1等人2014年的文章 1.简介 如今,人们对高功率半导体器件的兴趣日益浓厚,这些器件主要用于遥测、激光雷达系统或自由空间通信等应用。与固态激光器相比,半导体器件更紧凑且功耗更低,这在低功率供…...
【GPT】代谢概念解读
以下是对代谢中分解代谢和合成代谢两个概念的深入解读,用简单易懂的方式展开说明: 1. 分解代谢(Catabolism) 什么是分解代谢? 分解代谢是身体把大分子“拆开”的过程。就像把一个三明治分解成面包片、肉片和菜叶&#…...

Devops-git篇-01-git环境配置
环境配置 设置用户签名 配置用户名: git config --global user.name 你的用户名 配置邮箱: git config --global user.email 注册的邮箱 配置好之后,可以用git config --global --list命令查看配置是否OK $ git config --global --list u…...

STM32 HAL库开发学习1.STM32CubeMX 新建工程
STM32 HAL库开发学习1.STM32CubeMX 新建工程 一、 STM32 CubeMX 下载二、CubeMX 功能介绍1. 固件包路径设置2. 新建工程 三、创建项目实例1. 新建项目2. GPIO 管脚设置3. GPIO 窗口配置4. 调试设置5. 时钟配置6. 项目管理(1)项目信息(2&#…...

JS学习(2)(浏览器执行JS过程、JS的ECMAScript、DOM、BOM)
目录 一、浏览器如何执行JS? (1)浏览器主要的组成部分。 1、渲染引擎。 2、JS引擎。 (2)演示。 二、JS的组成。 (1)JS主要由三部分组成。 1、JS基础。 2、JS-API。 (2)EC…...
如何解决服务器扫描出的ASP木马问题
随着互联网的发展,网站安全问题日益凸显。其中,ASP(Active Server Pages)木马因其隐蔽性和危害性成为攻击者常用的手段之一。本文将详细介绍如何检测和清除服务器上的ASP木马,以保障网站的安全。 1. ASP木马概述 ASP…...
SpringBoot 架构助力夕阳红公寓管理系统可持续发展战略
摘 要 如今社会上各行各业,都在用属于自己专用的软件来进行工作,互联网发展到这个时候,人们已经发现离不开了互联网。互联网的发展,离不开一些新的技术,而新技术的产生往往是为了解决现有问题而产生的。针对于夕阳红公…...
TCP、HTTP、RPC
一、TCP (Transmission Control Protocol) 定义 TCP(传输控制协议)是一种面向连接、可靠传输的传输层协议,用于在计算机网络中提供端到端的数据通信服务。它是互联网协议套件的一部分,与IP(互联网协议)一…...
《C++ 中 RNN 及其变体梯度问题的深度剖析与解决之道》
在当今人工智能蓬勃发展的浪潮中,递归神经网络(RNN)及其变体长短期记忆网络(LSTM)和门控循环单元(GRU)在处理序列数据方面展现出了强大的潜力。然而,当我们在 C中着手实现这些网络时…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...