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

深入挖掘C++中的特性之一 — 继承

目录

1.继承的概念

2.举个继承的例子

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

1.父类成员的访问限定符对在子类中访问父类成员的影响

2.父类成员的访问限定符+子类的继承方式对在两个类外访问子类中父类成员的影响

4.继承类模版(注意事项)

5.父类与子类间的转换

6.继承中的作用域(主讲隐藏)

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

1.子类中的构造函数

2.子类中的拷贝构造函数

3.子类中的赋值运算符重载

4.子类中的析构函数

8.不能被继承的类

9.继承与友元

10.继承与静态成员

11.多继承及其菱形继承问题

0.简单介绍

1.单继承模型

2.多继承模型

3.菱形继承模型

4.二义性例子

5.虚继承

12.继承和组合


1.继承的概念

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

2.举个继承的例子

下面我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复用这些成员,就不需要重复定义了,省去了很多麻烦。

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; // 职称 
};

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

1.父类成员的访问限定符对在子类中访问父类成员的影响

1.父类成员public、protected修饰:子类中可以访问父类的成员变量。

2.父类成员private修饰:子类中不可以访问父类的成员变量。

2.父类成员的访问限定符+子类的继承方式对在两个类外访问子类中父类成员的影响

1.private继承:在两个类外访问不了。

2.除了private继承以外的其它继承方式:

父类成员在子类中的访问方式==min(成员在父类的访问限定符,继承方式),其中public>

protected>private。

4.继承类模版(注意事项)

namespace yx
{//用vector容器实现stack容器适配器template<class T>class stack : public std::vector<T>{public:void push(const T& x){// 基类是类模板时,需要指定⼀下类域, // 否则编译报错:error C3861: “push_back”: 找不到标识符 // 因为stack<int>实例化时,也实例化vector<int>了 // 但是模版是按需实例化,虽实例化vector<int>了,// 但vector<int>中的push_back等成员函数未实例化,所以找不到 vector<T>::push_back(x);//push_back(x);需要指定类域实例化模版参数}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};}

5.父类与子类间的转换

class Person
{
protected:string _name; // 姓名 string _sex; // 性别 int _age; // 年龄 
};class Student : public Person
{
public:int _No; // 学号 
};

1.public继承的派生类对象、指针、引用可以赋值给基类的对象、指针、引用;(切片)(基类指针或引用指向的是派生类中切出来的基类那部分)

2.基类对象不能赋值给派生类对象;

3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。

Student sobj;
// 1.派⽣类对象、指针、引用可以赋值给基类的对象、指针、引⽤ 
Person pobj = sobj;
Person* pp = &sobj;
Person& rp = sobj;
//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错 
sobj = pobj;
//3.可以这么写
Student sobj;
Person* pp = &sobj;
Person& rp = sobj;
Student* sp = (Student*)pp;
Student& rs = (Student&)rp;

6.继承中的作用域(主讲隐藏)

// 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;
}

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

在讨论子类的默认成员函数时,我们只需要按照之前看默认成员函数的方式+将父类中的那些成员看作是子类中的一个对象来看就可以。

1.子类中的构造函数

class Person
{
public:Person(const char* name): _name(name){cout << "Person()" << endl;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 子类中默认生成的构造函数的行为// 1、内置类型->不确定// 2、自定义类型->调用默认构造// 3、继承的父类成员看做一个整体对象,要求调用父类的默认构造//自己写的子类构造Student(const char* name, int num, const char* addrss):Person(name)//必须显式调用父类的构造函数, _num(num), _addrss(addrss){}protected:int _num = 1; //学号string _addrss = "武汉市洪山区";
};

2.子类中的拷贝构造函数

class Person
{
public:Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 严格说Student拷贝构造默认生成的就够用了// 如果有需要深拷贝的资源,才需要自己实现Student(const Student& s)//注意:这里写成了初始化列表的方式,
//但请不要混淆(这里并不是初始化的意思,
//但如果写成了初始化列表的方式,这里没有写Person(s)的话,将会调用Person类的默认构造函数):Person(s)//必须显式调用父类的拷贝构造函数, _num(s._num), _addrss(s._addrss){// 深拷贝}protected:int _num = 1; //学号string _addrss = "武汉市洪山区";
};

3.子类中的赋值运算符重载

class Person
{
public:Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 严格说Student赋值重载默认生成的就够用了// 如果有需要深拷贝的资源,才需要自己实现Student& operator=(const Student& s){if (this != &s){// (注意)规定:父类和子类的operator=构成隐藏关系,故这里需要指定类域。Person::operator=(s);//这里要显式调用父类的operator=()函数//这里的s变量传给父类的函数,用到了刚才讲的父类与子类间的转换的知识_num = s._num;_addrss = s._addrss;}return *this;}protected:int _num = 1; //学号string _addrss = "武汉市洪山区";
};

4.子类中的析构函数

class Person
{
public:~Person(){cout << "~Person()" << endl;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 严格说Student析构默认生成的就够用了// 如果有需要显示释放的资源,才需要自己实现// 析构函数都会被特殊处理成destructor() ~Student(){_addrss.~string();// (注意)规定:子类的析构和父类析构函数也构成隐藏关系// 规定:子类中不需要显示调用父类的析构,子类析构函数之后,会自动调用父类析构// 这样保证析构顺序,先子后父,显示调用取决于实现的人,不能保证先子后父// 先子后父//Person::~Person();//指定类域(因为隐藏)}
protected:int _num = 1; //学号string _addrss = "国庆快乐! 祖国万岁!";
};

8.不能被继承的类

C++11新增了⼀个final关键字,final修改基类,派生类就不能继承了。

// C++11的⽅法 
class Base final
{
public:void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
};

9.继承与友元

友元关系不能继承到子类。

10.继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有⼀个这样的成员。无论派生出多少个派生类,都只有⼀个static成员实例。

11.多继承及其菱形继承问题

0.简单介绍

单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承

多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。

菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以看出菱形继承有数据冗余和⼆义性的问题,在下图的Assistant的对象中Person成员会有两份。⽀持多继承就可能会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

1.单继承模型

2.多继承模型

3.菱形继承模型

4.二义性例子

class Person
{
public:string _name; // 姓名 
};class Student : public Person
{
protected:int _num; //学号 
};class Teacher : public Person
{
protected:int _id; // 职⼯编号 
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程 
};int main()
{// 编译报错:error C2385: 对“_name”的访问不明确 Assistant a;//a._name = "peter";会报错// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决 a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

5.虚继承

class Person
{
public:string _name; // 姓名 
};// 使⽤虚继承Person类 
class Student : virtual public Person
{
protected:int _num; //学号 
};// 使⽤虚继承Person类 
class Teacher : virtual public Person
{
protected:int _id; // 职⼯编号 
};// 教授助理 
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程 
};int main()
{// 使⽤虚继承,可以解决数据冗余和⼆义性 Assistant a;a._name = "peter";return 0;
}

举个例子:

注意:谁有数据冗余和二义性,就在继承它的时候加上virtual,使用虚继承,可以解决数据冗余和⼆义性

由上图可以看出A有数据冗余和二义性,所以就在继承A的位置B和C加上virtual

12.继承和组合

相关文章:

深入挖掘C++中的特性之一 — 继承

目录 1.继承的概念 2.举个继承的例子 3.继承基类成员访问方式的变化 1.父类成员的访问限定符对在子类中访问父类成员的影响 2.父类成员的访问限定符子类的继承方式对在两个类外访问子类中父类成员的影响 4.继承类模版&#xff08;注意事项&#xff09; 5.父类与子类间的转…...

Linux 下 poll 详解

在Linux系统编程中&#xff0c;poll 是一个强大的多路复用&#xff08;I/O 多路复用&#xff09;函数&#xff0c;用于同时监控多个文件描述符的事件&#xff0c;特别是在处理网络套接字或其他I/O设备时。相比于select&#xff0c;poll 支持监控更多的文件描述符&#xff0c;并…...

virtualbox配置为NAT模式后物理机和虚拟机互通

virtualbox配置为 NAT模式后&#xff0c;虚拟机分配到的 IP地址一般是 10.xx网段的&#xff0c;虚拟机可以通过网络地址转换访问物理机所在的网络&#xff0c;但若不做任何配置&#xff0c;则物理机无法直接访问虚拟机。 virtualbox在提供 NAT配置模式时&#xff0c;也提供了端…...

工程机械车辆挖掘机自卸卡车轮式装载机检测数据集VOC+YOLO格式2644张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2644 标注数量(xml文件个数)&#xff1a;2644 标注数量(txt文件个数)&#xff1a;2644 标注…...

[Notepad++] 文本编辑器的下载及详细安装使用过程(附有下载文件)

程序员常用的文本编辑器Notepad&#xff0c;用于修改配置文件等 下载链接在文末 下载压缩包后解压 &#xff01;&#xff01;安装路径不要有中文 解压文件&#xff0c;得到 双击exe文件 选择简体中文&#xff0c;点击OK 点击下一步 点击“我接受” 更改安装目录&#xff0c;不…...

深入浅出Java多线程(六):Java内存模型

引言 大家好&#xff0c;我是你们的老伙计秀才&#xff01;今天带来的是[深入浅出Java多线程]系列的第六篇内容&#xff1a;Java内存模型。大家觉得有用请点赞&#xff0c;喜欢请关注&#xff01;秀才在此谢过大家了&#xff01;&#xff01;&#xff01; 在并发编程中&#xf…...

注册了个小趴菜999#it#com

注册了个 999#it#com 拿着玩玩吧 现在二级域名竟然也让注册了 不过cn.com的二级似乎早就可以了...

UE4 材质学习笔记02(数据类型/扭曲着色器)

一.什么是数据类型 首先为啥理解数据类型是很重要的。一些节点的接口插槽只接受特定类型的数据&#xff0c;如果连接了不匹配的数据就会出现错误&#xff0c;有些接口可以接受任何数据类型&#xff0c;但是实际上只会使用到其中的一些。并且有时可以将多个数据流合并成一个来编…...

Linux驱动开发(速记版)--设备树插件

第六十八章 设备树插件介绍 Linux 4.4之后引入了动态设备树&#xff0c;其中的设备树插件&#xff08;Device Tree Overlay&#xff09;是一种扩展机制&#xff0c;允许在运行时动态添加、修改或删除设备节点和属性。 设备树插件机制通过DTS&#xff08;设备树源文件&#xff0…...

代码报错后如何定位问题

文章目录 一、查看终端报错Exception二、百度三、问 一、查看终端报错Exception 代码报错时&#xff0c;终端一般都会有xxxException异常提示&#xff0c;或者exception、error…等字样提示&#xff0c;就顺着这些关键字提醒找到异常即可。 二、百度 不知道这个英文的异常是…...

Python数据可视化--Matplotlib--入门

我生性自由散漫&#xff0c;不喜欢拘束。我谁也不爱&#xff0c;谁也不恨。我没有欺骗这个&#xff0c;追求那个&#xff1b;没有把这个取笑&#xff0c;那个玩弄。我有自己的消遣。 -- 塞万提斯 《堂吉诃德》 Matplotlib介绍 1. Matplotlib 是 Python 中常用的 2D 绘图库&a…...

美国食品等级FDA认证测试介绍

美国FDA认证概览 美国食品和药物管理局&#xff08;FDA&#xff09;是负责监管食品、药品、医疗设备和化妆品等的联邦机构&#xff0c;以确保这些产品对公众健康和安全的影响。FDA认证在美国属于强制性认证&#xff0c;对产品的安全性和质量有着严格的要求。通过FDA认证&#…...

Vue2如何在网页实现文字的逐个显现

目录 Blue留言&#xff1a; 效果图&#xff1a; 实现思路&#xff1a; 代码&#xff1a; 1、空字符串与需渲染的字符串的定义 2、vue的插值表达式 3、函数 4、mounted()函数调用 结语&#xff1a; Blue留言&#xff1a; 在国庆前夕&#xff0c;突发奇想&#xff0c;我想…...

mybatisplus的查询,分页查询,自定义多表查询,修改的几种写法

使用mybatisplus的Db类简化写法 使用静态调用的方式&#xff0c;执行CRUD方法&#xff0c;避免Spring环境下Service循环注入、简洁代码&#xff0c;提升效率需要项目中已注入对应实体的BaseMapper完整使用方式见官方测试用例&#xff1a;官方测试用例地址对于参数为Wrapper的&…...

括号匹配判断

本题实现求表达式中括号是否匹配。只需判断表达式中括号&#xff08;本题中只会出现三种括号&#xff0c;分别是小括号&#xff0c;中括号和大括号&#xff09;是否匹配&#xff0c;表达式中可以有其他值也可没有。 函数接口定义&#xff1a; int match (char *exp); 其中 …...

数据结构(栈和队列的实现)

1. 栈&#xff08;Stack&#xff09; 1.1 栈的概念与结构 栈是一种特殊的线性表&#xff0c;其只允许固定的一段插入和删除操作&#xff1b;进行数据插入和删除的一段叫做栈顶&#xff0c;另一端叫栈底&#xff1b;栈中的元素符合后进先出LIFO&#xff08;Last In First Out&…...

Python批量处理客户明细表格数据,挖掘更大价值

批量处理 .xls 数据并进行归类分析以挖掘内在价值&#xff0c;通常涉及以下步骤&#xff1a; 读取数据&#xff1a;使用 pandas 库读取 .xls 文件。数据清洗&#xff1a;处理缺失值、异常值、重复值等。数据转换&#xff1a;对数据进行必要的转换&#xff0c;如日期格式统一、…...

NAND Flash虚拟层索引表机制

​​​​​ NAND Flash虚拟层的索引表用于建立逻辑块与数据块、日志块之间的关系,用于NAND Flash虚拟层在运行过程中的读写、擦除操作;由于NAND Flash虚拟层采用集中索引的方式,因此在NAND Flash虚拟层启动时需要在NAND Flash存放索引表区域扫描并确定NAND Flash中存…...

Spring Boot框架:新闻推荐系统开发新趋势

3系统分析 3.1可行性分析 通过对本新闻推荐系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本新闻推荐系统采用JAVA作为开发语言&#xff0c;Spring Boot框…...

RK3568平台(opencv篇)opencv处理图像

一.颜色转换 cv2.cvtColor()函数功能: 将一幅图像从一个色彩空间转换到另一个色彩空间。 函数原型: cv2.cvtColor(src,code,dst=None,dstCn=None) 参数定义: src:要转换的源文件 code,转换的色彩空间,在 opencv 中有超过 150 种颜色空间转换方法,但是经常用的只有 B…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

热烈祝贺埃文科技正式加入可信数据空间发展联盟

2025年4月29日&#xff0c;在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上&#xff0c;可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞&#xff0c;强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...