【C++进阶学习】第二弹——继承(下)——挖掘继承深处的奥秘
继承(上):【C++进阶学习】第一弹——继承(上)——探索代码复用的乐趣-CSDN博客
前言:
在前面我们已经讲了继承的基础知识,让大家了解了一下继承是什么,但那些都不是重点,今天,我们一起来挖掘一下继承底层的一些知识和一些极容易出错的点
目录
一、隐藏
1.1 隐藏的概念
1.2 隐藏的两种类型
二、派生类的默认成员函数
三、继承与友元
四、继承与静态成员
五、总结
一、隐藏
1.1 隐藏的概念
在 C++ 中,继承是一种机制,使得子类可以继承父类的成员变量和成员函数。然而,当子类中出现和父类同名的成员变量或成员函数时,会发生一种特殊的现象,即隐藏。
隐藏是指:如果子类中出现了与父类同名的成员变量或成员函数,则子类中的这个成员会“隐藏”父类中的同名成员,使得父类中的同名成员在子类中不可见。
1.2 隐藏的两种类型
具体来说,有以下两种情况:
成员变量隐藏:
如果子类中出现了和父类同名的成员变量,则子类中的这个成员变量会隐藏父类中的同名成员变量。例如:
class Parent {
public:int a;
};class Child : public Parent {
public:int a; // 此处的 a 会隐藏 Parent 中的 a
};int main() {Child c;c.a = 10; // 此处修改的是 Child 中的 a,而不是 Parent 中的 areturn 0;
}
成员函数隐藏:
如果子类中出现了和父类同名的成员函数,则子类中的这个成员函数会隐藏父类中的同名成员函数。例如:
class Parent {
public:void func() {cout << "Parent::func()" << endl;}
};class Child : public Parent {
public:void func() {cout << "Child::func()" << endl;}
};int main() {Child c;c.func(); // 此处调用的是 Child 中的 func(),而不是 Parent 中的 func()return 0;
}
需要注意的是,虽然子类中的成员会隐藏父类中的同名成员,但是父类中的成员仍然存在,只是在子类中不可见。如果想在子类中访问父类中被隐藏的成员,可以使用作用域运算符(::)来显式地指明要访问的成员所在的类。例如:
class Parent {
public:int a;
};class Child : public Parent {
public:int a;
};int main() {Child c;c.a = 10; // 此处修改的是 Child 中的 ac.Parent::a = 20; // 此处修改的是 Parent 中的 areturn 0;
}
总之,在 C++ 中的继承中,隐藏是一种特殊的机制,需要注意避免误用。
二、派生类的默认成员函数
在 C++ 中,当我们定义一个类时,可以省略掉其中的成员函数的实现,而直接在类定义的外部提供其实现。这种情况下,如果我们不提供任何实现,那么 C++ 编译器会自动为我们提供一个默认的构造函数、析构函数和拷贝构造函数和拷贝赋值运算符。
对于派生类来说,情况也是类似的。当我们定义一个派生类时,如果我们不提供任何构造函数,那么 C++ 编译器会自动为我们提供一个默认的构造函数,其构造函数的参数列表和父类的构造函数一致。例如:
class Parent {
public:Parent(int a) : m_a(a) {}int m_a;
};class Child : public Parent {
public:Child() : Parent(0) {} // 此处的构造函数会自动调用 Parent 的构造函数,并传入 0 作为参数
};
同时,如果我们没有提供任何析构函数,那么 C++ 编译器也会自动为我们提供一个默认的析构函数,其析构函数的函数体为空。例如:
class Parent {
public:~Parent() {}
};class Child : public Parent {
public:~Child() {} // 此处的析构函数会自动调用 Parent 的析构函数
};
需要注意的是,如果我们提供了任何一个构造函数或析构函数,那么 C++ 编译器就不会再为我们提供默认的构造函数或析构函数了。这时如果我们需要使用默认的构造函数或析构函数,需要我们自己显式地提供。(析构顺序为先派生类再基类)
另外,对于拷贝构造函数和拷贝赋值运算符来说,如果我们没有提供任何拷贝构造函数和拷贝赋值运算符,那么 C++ 编译器会自动为我们提供一个默认的拷贝构造函数和拷贝赋值运算符,其行为是浅拷贝(Shallow Copy),即直接拷贝成员变量的值。例如:
class Parent {
public:Parent(int a) : m_a(a) {}int m_a;
};class Child : public Parent {
public:Child(int a, int b) : Parent(a), m_b(b) {}int m_b;
};int main() {Child c1(1, 2);Child c2(c1); // 此处调用的是 Child 的默认拷贝构造函数,直接拷贝 m_a 和 m_b 的值return 0;
}
但是,如果我们的类中有指针类型的成员变量,那么默认的拷贝构造函数和拷贝赋值运算符就会出现问题,因为它们只会拷贝指针的值,而不会拷贝指针所指向的内存。这时我们需要自己提供一个拷贝构造函数和拷贝赋值运算符,实现深拷贝(Deep Copy)。例如:
class Parent
{
public:Parent(int a):_a(a){}int _a;
};
class Child :public Parent
{
public:int* _b;Child(int a, int b):Parent(a),_b(new int(b)) //深度拷贝{}~Child(){delete _b;_b = nullptr;}Child(const Child& c):Parent(c){_b = new int(*c._b);}Child& operator=(const Child& c){if (this != &c){_a = c._a;delete _b;_b = new int(*(c._b)); }return *this;}void Print(){cout << "_a:" << _a << " " << "_b:" << *_b << endl;}
};
int main()
{Child c1(1, 2); Child c2(c1); //此处调用的是c2的深度拷贝c2.Print();Child c3 = c1;c3.Print();return 0;
}
运行结果:

我们通过两张图来总结一下:


三、继承与友元
在C++中,友元关系不能被继承,因为友元关系是独立于类定义的,并不是类的成员。因此,如果在父类中声明了一个友元函数或友元类,子类无法继承这种关系。
下面是一些相关知识点:
1、友元函数不能是成员函数:友元函数不是类的成员函数,因此不能使用this指针,也不能直接访问类的私有成员。需要通过类的对象或引用来访问私有成员。
2、友元关系不具有传递性:如果A类声明了B类为友元,则B类不会自动成为A类的友元。
友元函数可以是模板函数:模板函数可以被声明为类的友元,这样模板函数可以访问类的私有成员。
3、友元类:如果一个类声明另一个类为友元,则该友元类的所有成员函数都可以访问原类的私有成员。
4、友元不能继承:由于友元关系不是类的成员,因此不能被继承。如果在父类中声明了一个友元函数或友元类,子类无法继承这种关系。但子类可以在自己的范围内重新声明该友元关系。
示例:
#include<iostream>
#include<string>
using namespace std;
class Base {
public: friend void friendFunction(Base&);
protected:int value;
};class Derived : public Base {
public:// friendFunction 在 Derived 中不是友元函数,需要重新声明friend void friendFunction(Derived&);
};void friendFunction(Base& base) {// 可以访问 Base 的私有成员base.value = 10;
}void friendFunction(Derived& derived) {// 可以访问 Derived 的私有成员derived.value = 20;
}int main() {Derived derived;friendFunction(derived);return 0;
}
在上面的示例中,由于友元关系不能被继承,因此在Derived类中需要重新声明friendFunction函数为友元函数,以便在Derived类的实例上调用该函数。
四、继承与静态成员
在 C++ 中,类可以包含静态成员变量和静态成员函数,其中静态成员变量属于类本身,而不是类的某个对象,因此它们可以在不创建类对象的情况下被访问。
当一个类继承另一个类时,子类可以继承其父类的静态成员,并且可以在子类中重新定义这些静态成员。在这种情况下,子类和父类各自拥有自己的静态成员变量,它们之间没有任何关系。
下面是一个简单的例子:
#include<iostream>
using namespace std;class Parent {
public:static int a;
};int Parent::a = 10; //静态成员的定义只能在类外进行class Child : public Parent {
public:static int a; //类中只能声明静态成员
};int Child::a = 20; //静态成员的定义只能在类外进行int main() {cout << Parent::a << endl; //输出10cout << Child::a << endl; //输出20return 0;
}
运行结果:

在上面的例子中,Parent 类和 Child 类都有一个静态成员变量 a,它们各自拥有自己的实现。在 main 函数中,我们可以直接通过类名来访问这些静态成员变量。
需要注意的是,如果子类中没有重新定义父类的静态成员变量,那么子类可以直接访问父类的静态成员变量,例如 Parent::a。如果子类重新定义了父类的静态成员变量,那么子类只能访问自己的静态成员变量,例如 Child::a。
此外,静态成员函数也可以继承,并且可以在子类中重新定义。在子类中重新定义父类的静态成员函数时,子类的静态成员函数会隐藏父类的静态成员函数,因此如果在子类中需要调用父类的静态成员函数,需要使用作用域运算符 :: 来显式地调用。
还有一个需要注意的点就是,类中只能声明静态成员,静态成员的定义只能在类外进行。
总之,在 C++ 中,静态成员在继承中的行为与普通成员有所不同,需要注意其使用方法。
五、总结
以上就是C++继承中需要额外注意的点,此外,还有一个很重要的知识点我们还没讲到——多继承、菱形继承、虚拟继承,这几个知识点是有很大关联性的,且我们在平时使用继承时也很容易出错,鉴于篇幅问题,这几个问题会在下一篇单拎出来来讲,今天的内容就到此为止。
感谢各位大佬观看,创作不易,还请各位大佬一键三连!!!

相关文章:
【C++进阶学习】第二弹——继承(下)——挖掘继承深处的奥秘
继承(上):【C进阶学习】第一弹——继承(上)——探索代码复用的乐趣-CSDN博客 前言: 在前面我们已经讲了继承的基础知识,让大家了解了一下继承是什么,但那些都不是重点,今…...
LangChain-ChatGLM本地搭建|报错合集(win10)
安装过程 1. 创建虚拟环境 conda create -n langchain-chatglm python3.10 conda activate langchain-chatglm2. 部署 langchain-ChatGLM git clone https://github.com/imClumsyPanda/langchain-ChatGLMpip3 install -r requirements.txt pip3 install -U gradio pip3 inst…...
IP地址简介
一、IP地址 Internet Protocol Address,即网络层协议地址,是IP的缩写。 二、IP地址的作用 为什么不直接使用MAC,又加了一个IP地址呢? 事实上底层传输,最终使用的肯定是MAC地址,但是由于在以前&#x…...
谈吐的艺术
被人表扬,该怎么回应 你越是说自己其实没那么好, 对方出于客气, 就越是要证明你其实比你说的好得多。 O可能遇到的问题 每当工作和学习上做出点成绩,有人夸奖我的时候,我都会觉得很尴尬。因为不谦虚会得罪人ÿ…...
Linux 和 分区
文章目录 流程挂载设备文件名 Linux 下各分区的含义家目录 流程 在windows中,一个硬盘要使用只需要分区、格式化之后就可以使用了 在linux中,除了分区和格式化之外,还需要一个叫挂载的操作 挂载 挂载,就相当于windows环境下的写…...
⭐ ▶《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch3 贝尔曼最优公式 【压缩映射定理】
PPT 截取必要信息。 课程网站做习题。总体 MOOC 过一遍 1、视频 学堂在线 习题 2、过 电子书,补充 【下载:本章 PDF 电子书 GitHub 界面链接】 [又看了一遍视频] 3、总体 MOOC 过一遍 习题 学堂在线 课程页面链接 中国大学MOOC 课程页面链接 B 站 视频链…...
Pikachu上的CSRF以及NSSCTF上的[NISACTF 2022]bingdundun~、 [SWPUCTF 2022 新生赛]xff
目录 一、CSRF CSRF(get) login CSRF(post) CSRF Token 二、CSRF的相关知识点 (1)什么是CSRF? (2)工作原理 (3)CSRF漏洞形成的条件 1、用户要在登录状态(即浏览器保存了该…...
大数据分析-二手车用户数据可视化分析
项目背景 在当今的大数据时代,数据可视化扮演着至关重要的角色。随着信息的爆炸式增长,我们面临着前所未有的数据挑战。这些数据可能来自社交媒体、商业交易、科学研究、医疗记录等各个领域,它们庞大而复杂,难以通过传统的数据处…...
AI训练Checkpoint对存储的影响
检查点(Checkpoints)是机器学习和深度学习训练过程中的一个重要机制,旨在定期保存训练状态,以便在训练过程中遇到失败或中断时能够从中断处恢复训练,而无需从头开始。 随着模型参数量的剧增,Checkpoint文件…...
Python笔记 - 正则表达式
正则表达式(Regular Expression,简称regex)是一种强大的工具,用于匹配字符串模式。在Python中,正则表达式通过re模块提供。本文将带你深入了解Python中的正则表达式,从基础概念到高级用法。 1. 什么是正则…...
安卓网络通信(多线程、HTTP访问、图片加载、即时通信)
本章介绍App开发常用的以下网络通信技术,主要包括:如何以官方推荐的方式使用多线程技术,如何通过okhttp实现常见的HTTP接口访问操作,如何使用Dlide框架加载网络图片,如何分别运用SocketIO和WebSocket实现及时通信功能等…...
Virtual Memory Primitives for User Program翻译
Virtual Memory Primitives for User Program 安德鲁阿普尔(Andrew Appel)和李凯(Kai Li) 普林斯顿大学计算机科学系 摘要 传统上,内存管理单元(MMUS)被操作系统用于实现磁盘分页的虚拟内存…...
网络基础2
目录 应用层HTTP协议认识URLurlencode和urldecode HTTP协议格式http请求格式http响应格式 HTTP的方法GET与POST的区别 HTTP的状态码HTTP常见HeaderCookie与Session 传输层在谈端口号端口号范围划分认识知名端口号netstatpidof UDP协议UDP协议端格式UDP的特点面向数据报UDP的缓冲…...
C# 下载文件2
从服务下载压缩包 过程 发起请求 HttpWebRequest 断点续传 HttpWebRequest.AddRange() 获取服务资源的响应 HttpWebResponse 设置下载进度条 解压压缩包 ZipFile using System; using System.IO; using System.IO.Compression; using System.Net;namespace Test01 {clas…...
Unity | Tilemap系统
目录 一、准备工作 1.插件导入 2.资源导入 二、相关组件介绍 1.Grid组件 2.Tilemap组件 3.Tile 4.Tile Palette 5.Brushes 三、动态创建地图 四、其他功能 1.移动网格上物体 2.拖拽缩放地图 Unity Tilemap系统为2D游戏开发提供了一个直观且功能强大的平台ÿ…...
CSS选择符和可继承属性
属性选择符: 示例:a[target"_blank"] { text-decoration: none; }(选择所有target"_blank"的<a>元素) /* 选择所有具有class属性的h1元素 */ h1[class] { color: silver; } /* 选择所有具有hre…...
C++升级软件时删除老版本软件的桌面快捷方式(附源码)
删除桌面快捷方式其实是删除桌面上的快捷方式文件,那我们如何去删除桌面快捷方式文件呢?软件可能已经发布过多个版本,其中的一些版本的快捷方式文件名称可能做了多次改动,程序中不可能记录每个版本的快捷方式名称,没法直接去删除快捷方式文件。本文就给出一种有效的处理办…...
github国内加速访问有效方法
这里只介绍实测最有效的一种方法,修改主机的Hosts文件,如果访问github网站慢或者根本无法访问的时候可以采用下面方法进行解决。 1、搜索一个IP查询网站 首先百度搜索选择一个IP查询的网站,这里我用下面这个网站(如果该网站失效…...
如何处理JavaScript中的浮点数精度问题
在开发过程中,特别是涉及到金额计算或需要精确比较的场景,浮点数精度问题是一个常见而重要的挑战。本文将介绍在JavaScript中如何识别、理解和解决这些问题,并提供一些实用的技巧和建议。 1. 问题背景 JavaScript中的浮点数采用IEEE 754标准…...
ASPICE标准与ASPICE认证:提升汽车软件开发质量与效率的关键途径
在当今日新月异的科技时代,软件产品的质量和可靠性成为了企业赢得市场的关键。而ASPICE(Automotive SPICE)标准,作为汽车行业中软件过程评估的国际通用标准,正逐渐引起行业的广泛关注。那么,ASPICE标准究竟…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
