C++笔记---继承(上)
1. 继承的简单介绍
1.1 继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。
简单来说,被继承的类叫做父类(或基类),继承自父类的类叫做子类(或派生类)。
子类拥有父类的所有成员,在此基础之上可以对父类进行拓展。
1.2 子类的定义方式
class 子类名 : 访问限定符 父类名
{// 拓展内容
}
通常来说,父类和子类具有类别上的包含关系。
例如,老师和同学不仅具有人的基本特点,还在人的基础之上有了自己的拓展,而老师和同学都属于人。
我们在用C++进行描述的时候,就可以将老师和同学设计成人的子类:
人类(父类):
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "张三"; // 姓名string _address; // 地址string _tel; // 电话int _age = 18; // 年龄
};
学生类(子类):
class Student : public Person
{
public:// 学习void study(){// ...}
protected:int _stuid; // 学号
};
老师类(子类):
class Teacher : public Person
{
public:// 授课void teaching(){//...}
protected:string title; // 职称
};
1.3 继承方式
访问限定符限定的是继承的方式,不同访问限定符下的继承方式如下:
| 类成员 / 继承方式 | public | protected | private |
| 父类的public成员 | 子类的public成员 | 子类的protected成员 | 子类的private成员 |
| 父类的protected成员 | 子类的protected成员 | 子类的protected成员 | 子类的private成员 |
| 父类的private成员 | 子类无法直接显式访问 | 子类无法直接显式访问 | 子类无法直接显式访问 |
子类成员的访问权限:public > protected > private > 父类中被修饰为private
其中,protected访问限定符是伴随着继承的出现而出现的。被它修饰的成员,意味着无法在类外部进行访问,而可以在类内部或其子类中被访问。
无论子类以何种方式继承,在父类中被修饰为private的成员子类都不可直接显式访问。
继承方式用于限定继承下来的成员访问权限不能高于继承方式。
当不指定继承方式时,class子类默认为private方式继承,struct子类默认为public方式继承。
在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类类里使用,实际中扩展维护性不强。
可以采用protected/private继承的一个例子:
#include<vector>
#define CONTAINER std::vectortemplate<class T>
class stack : private CONTAINER<T>
{
public:void push(const T& x){CONTAINER<T>::push_back(x);}void pop(){CONTAINER<T>::pop_back();}const T& top(){return CONTAINER<T>::back();}bool empty(){return CONTAINER<T>::empty();}
};int main()
{stack<int> st;st.push(1);st.push(2);st.push(3);while (!st.empty()){cout << st.top() << " ";st.pop();} return 0;
}
这里采用了继承vector的方式来实现stack,但我希望用户只用stack的接口而不直接访问vector的成员函数,此时就可以采取protected/private的继承方式。
1.4 继承类模板
相信细心的小伙伴已经发现了,在上面stack的例子中,我们每次调用vector的接口时都对其类域进行了指定,否则会发生编译报错:
error C3861: “push_back”: 找不到标识符
这是因为,模板是按需实例化的,当类模板中的函数没有被调用时,其就不会实例化。
我们在实例化stack<T>对象时,vector<T>对象也跟着实例化。但是vector<T>中,只有成员变量和构造函数被实例化了。
我们在调用push_back()函数时,由于this指针为stack<T>类型,所以编译器并不会将vector模板中的push_back()函数实例化,而是直接去寻找其定义或声明。
所以,继承模板类时,调用父类函数要注意指定类域。
2. 父类和子类的转换
1. public继承的子类对象可以赋值给父类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把子类中父类那部分切出来,父类指针或引用指向的是子类中切出来的父类那部分。
2. 父类对象不能赋值给子类对象。
3. 父类的指针或者引用可以通过强制类型转换赋值给子类的指针或者引用。但是必须是父类的指针是指向子类对象时才是安全的。(这里父类如果是多态类型,可以使用RTTI(Run-Time TypeInformation)的dynamic_cast 来进行识别后进行安全转换)
class Person
{
protected:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};class Student : public Person
{
public:int _No; // 学号
};int main()
{Student sobj;// 1.派⽣类对象可以赋值给基类的指针/引⽤Person* pp = &sobj;Person& rp = sobj;// ⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的Person pobj = sobj;//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错sobj = pobj;return 0;
}
3. 继承中的作用域及"隐藏"规则
隐藏规则:
1. 在继承体系中父类和子类都有独立的作用域。
2. 子类和基类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏。(在子类成员函数中,可以使用"父类::父类成员"显式访问)
// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆
class Person
{
protected:string _name = "⼩李⼦"; // 姓名int _num = 111; // ⾝份证号
};class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " ⾝份证号:" << Person::_num << endl;cout << " 学号:" << _num << endl;}
protected:int _num = 999; // 学号
};int main()
{Student s1;s1.Print();return 0;
};
这里,程序运行的结果为

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同(分别在父类和子类中定义的函数只会触发隐藏而不会触发函数重载)就构成隐藏。
4. 注意在实际中在继承体系里面最好不要定义同名的成员
class A
{
public:void fun(){cout << "func()" << endl;}
};class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};
此时,B中的fun函数会隐藏掉A中的fun函数,而不会构成重载。
4. 子类的默认成员函数
子类的默认成员函数主要是用于处理父类没有的成员成员变量,父类自身的成员交由其自己的默认成员函数去处理。
4.1 构造函数
子类在构造函数的初始化列表中可以显式调用父类的构造函数对父类成员变量进行初始化,若未显式调用则会调用父类的默认构造函数。
如果没有显式调用父类的构造函数,且父类没有默认构造函数,那么此时就会发生报错。
显式调用父类构造函数的方式是:
父类名(参数列表)
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;
}
4.2 赋值重载
必须要在子类的operator=中显式调用父类的operator=才能按照预期正常的对父类成员进行拷贝,否则只会完成浅拷贝。
要注意,子类的operator=会隐藏父类的operator=,调用父类的operator=需要指定类域。
Student& operator = (const Student& s)
{cout << "Student& operator= (const Student& s)" << endl;if (this != &s){// 构成隐藏,所以需要显⽰调⽤Person::operator =(s);_num = s._num;} return* this;
}
4.3 析构函数
父类的析构函数会在子类的析构函数被调用之后自动调用,无需显式调用。
相关文章:
C++笔记---继承(上)
1. 继承的简单介绍 1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。 继承呈…...
气膜体育馆:为学校打造智能化运动空间—轻空间
随着教育体制的逐步升级,学校在提升学生综合素质方面的需求日益增长,特别是在体育场地方面。气膜体育馆作为一种新型的运动空间形式,正在迅速成为学校体育设施的优选方案。凭借其快速搭建、节能环保等优势,气膜馆在全国各地的校园…...
JVM 调优篇5 jvm性能监控
一 jvm性能监控 1.1 概述 性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。 体会1:使用数据说明问题,使用知识分析问题,使用工具处理问…...
一. Unity实现虚拟摇杆及屏幕自适应功能
手游里面很多类型的游戏都需要用到遥感功能,例如王者荣耀,和平精英等,之前的摇杆功能都是用类似于Easy Touch的插件进行开发的,今天不借助任何插件来实现虚拟摇杆的功能。 一般虚拟摇杆的组成都是由轮盘和遥感的点组成,…...
【达梦数据库】mysql 和达梦 tinyint 与 bit 返回值类型差异
测试环境 mysql5.7.44 达梦2024Q2季度版 前言 在mysql 中存在 tinyint(1)的用法来实现存储0 1 作为boolean的标识列;但是在达梦并不允许使用 tinyint(1)来定义列,只能使用 tinyint 即 取值范围为ÿ…...
VUE工程中axios基本使用
安装axios npm install axios -s在main.js中引入 import http from axios Vue.prototype.$http = http将其绑定在VUE的prototype属性中 vue工程目录下,新建config文件夹,在config文件夹下新建index.js export default {...
跨服务器执行PowerShell脚本
本机和远程机都要执行 Enable-PSRemoting -Force 远程端关闭公用网络 Get-NetConnectionProfile Set-NetConnectionProfile -Name "未识别的网络" -NetworkCategory Private 本机和远程机都要执行 winrm quickconfig 将远程机ip加入信任列表 cd WSMan::localhost\…...
linux_L2_linux删除文件
linux 删除文件 在Linux下删除文件有多种实现方法,以下是其中几种常见的方法: 方法一:使用rm命令删除单个文件 rm 文件路径例如,删除当前目录下的文件file.txt: rm file.txtQuestion :当你在Linux系统中使用rm命令删…...
系统架构设计师 - 项目管理
项目管理 项目管理(1-3分,案例分析 25分)立项管理 ★盈亏平衡分析 范围管理 ★★时间管理 ★★★★概述前导图法 PDM(单代号网络图)箭线图法 ADM(双代号网络图) 了解关键路径法总时差自由时差 甘特图 成本管理 ★挣值管理概述指数计算 软件质…...
Spring Boot基础
项目创建 项目启动 请求响应 RestController 1.返回值处理 RestController:这个注解结合了Controller和ResponseBody的功能。它默认将所有处理请求的方法的返回值直接作为响应体内容返回,主要用于构建RESTful API。返回的数据格式通常是JSON或XML&…...
C语言 | Leetcode C语言题解之第402题移掉K位数字
题目: 题解: char* removeKdigits(char* num, int k) {int n strlen(num), top 0;char* stk malloc(sizeof(char) * (n 1));for (int i 0; i < n; i) {while (top > 0 && stk[top] > num[i] && k) {top--, k--;}stk[top]…...
使用Visual Studio Code配置C/C++开发环境的全面指南
目录 引言 一、准备工作 1. 安装Visual Studio Code 2. 安装C/C编译器 3. 配置环境变量(仅Windows用户) 二、在VS Code中安装C/C扩展 三、创建您的第一个C/C项目 1. 创建项目文件夹 2. 打开项目文件夹 3. 创建源文件 四、配置任务(…...
算法练习题26——多项式输出(模拟)
输入格式 输入共有 2 行 第一行 1 个整数,n,表示一元多项式的次数。 第二行有 n1 个整数,其中第 i 个整数表示第 n−i1 次项的系数,每两个整数之间用空格隔开。 输出格式 输出共 1 行,按题目所述格式输出多项式。…...
卷积神经网络经典模型架构简介
【图书推荐】《PyTorch深度学习与企业级项目实战》-CSDN博客 《PyTorch深度学习与企业级项目实战(人工智能技术丛书)》(宋立桓,宋立林)【摘要 书评 试读】- 京东图书 (jd.com) ImageNet是一个包含超过1 500万幅手工标记的高分辨率图像的数据…...
【Kubernetes】常见面试题汇总(十三)
目录 39.简述 Kubernetes Scheduler 使用哪两种算法将 Pod 绑定到 worker 节点? 40.简述 Kubernetes kubelet 的作用? 41.简述 Kubernetes kubelet 监控 Worker 节点资源是使用什么组件来实现的? 39.简述 Kubernetes Scheduler 使用哪两种算…...
嵌入式QT开发:构建高效智能的嵌入式系统
摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面…...
Linux抢占调度
目录 抢占流程 抢占时机 用户态抢占时机 1、 从系统调用返回用户空间 2、 从中断返回用户空间 内核态抢占时机 1、中断处理程序返回内核空间 可以看到最终是到了 preempt_schedule_irq 2、当内核从non-preemptible(禁止抢占)状态变成pr…...
k8s中,为什么把pod的服务以deployment的形式通过nodeport对外发布,以及容器和虚拟机的一些区别
deployment是个控制器 主要负责管理pod,来代表k8s集群向外提供稳定的服务。 说,k8s有很多优点。 说k8s的优点,可能先需要说容器提供的便利。 同样的硬件资源 跑几个虚拟机,每个虚拟机上跑几个服务。 就挺重了。风扇呼呼叫 …...
PMP--一模--解题--41-50
文章目录 14.敏捷--方法--回顾--回顾是最重要的一个实践,原因是它能让团队学习、改进和调整其过程。41、 [单选] 新项目中的所有团队成员都希望通过尽快交付价值来获得客户的信任。项目经理了解到一个资源已经在其他项目中与发起人一起工作。某资源似乎在使用个人影…...
Kafka启动关闭及其相关命令kafka启动、状态监控、日常操作
开启zookeeper命令(备注:先进入zookeeper的bin目录) ./zkServer.sh start 关闭zookeeper命令(备注:先进入zookeeper的bin目录) ./zkServer.sh stop Kafka启动命令(备注:先进入kafka目录) 常规模式启动kafka bin/kafka-server-start.sh config/server.properties 进程守…...
使用Nodejs和Taotoken快速构建一个支持多模型切换的聊天服务
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用Node.js和Taotoken快速构建一个支持多模型切换的聊天服务 基础教程类,面向全栈或后端开发者,教程将引导…...
智慧能耗管理系统:嵌入式工控机在工业节能中的核心应用
1. 工厂能耗管理的痛点与智能化转型契机 在制造业摸爬滚打十几年,我见过太多工厂在能耗管理上的“粗放式”经营。电费单是每个月固定的大额支出,但具体电用在了哪里,哪个车间、哪条产线、甚至哪台设备是“电老虎”,很多时候都是一…...
MIMO AONN架构:量子干涉实现超低功耗光学神经网络
1. MIMO AONN架构的核心价值光学神经网络(AONN)正在突破传统电子计算的物理极限。在传统电子神经网络中,非线性激活函数需要消耗大量能量进行电子-光子转换,而基于量子干涉的光学非线性机制可以直接在光域实现这一关键操作。我们实…...
工业多串口通信实战:基于EM9170的8串口方案设计与优化
1. 项目概述:为什么8串口在今天依然重要?在物联网、工业自动化、智能楼宇这些领域里摸爬滚打久了,你会发现一个有趣的现象:那些看似“古老”的通信接口,生命力往往比我们想象的要顽强得多。串口,或者说RS-2…...
收藏!小白程序员必看:读懂AI岗位JD,精准投递不陪跑
本文针对AI岗位认知模糊、JD理解困难等问题,为读者提供六步解析法,包括明确岗位性质、了解公司类型、评估薪资水平、硬性条件筛选、分析岗位职责和技能匹配。通过这些步骤,帮助读者精准定位适合自己的AI岗位,避免盲目投递。同时&a…...
新手避坑指南:用PEAK CAN卡和ROS快速上手大陆ARS408-21XX毫米波雷达
新手避坑指南:用PEAK CAN卡和ROS快速上手大陆ARS408-21XX毫米波雷达 毫米波雷达在自动驾驶和机器人感知领域扮演着关键角色,而大陆ARS408-21XX系列雷达因其高性价比和稳定性能,成为许多开发者的首选。然而,对于刚接触这一领域的新…...
【香橙派5】基于RKNN-Lite在RK3588上部署Yolov5的实战指南
1. 香橙派5与RK3588平台简介 香橙派5作为一款高性能的单板计算机,搭载了瑞芯微RK3588芯片,这颗芯片内置了强大的NPU(神经网络处理单元),算力高达6TOPS。这意味着它能够高效处理复杂的AI推理任务,比如实时目…...
如何快速构建工业通信系统:SECS4Net的完整实战指南
如何快速构建工业通信系统:SECS4Net的完整实战指南 【免费下载链接】secs4net SECS-II/HSMS-SS/GEM implementation on .NET 项目地址: https://gitcode.com/gh_mirrors/se/secs4net SECS4Net是一个基于.NET平台的开源库,完整实现了SEMI标准的SEC…...
第07章 FastMCP 把检索封装成 Agent 工具
第07章 FastMCP 把检索封装成 Agent 工具 工单知识库已经能在 Python 进程内被普通函数调用,但要让外部 Agent、Web 后端或其他语言的客户端使用这份能力,函数级别的接口不够:缺少协议、缺少描述、缺少跨进程通讯。MCP(Model Cont…...
ARMv8-AArch64 异常处理实战:从寄存器解析到调试技巧
1. ARMv8-AArch64异常处理入门指南 第一次接触ARMv8架构的异常处理时,我被那一堆寄存器搞得头晕眼花。ELR、ESR、FAR...这些缩写看起来就像天书一样。但经过几个实际项目的磨练后,我发现只要掌握几个关键点,异常处理其实并没有想象中那么难。…...
