当前位置: 首页 > 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…...

【移动端】Viewport 视口

1. 什么是 Viewport&#xff08;视口&#xff09;&#xff1f; Viewport&#xff08;视口&#xff09;是指浏览器中用户可见的那部分网页内容的区域&#xff0c;简单来说&#xff0c;它是用户当前看到的网页的“窗口”区域。在不同的设备上&#xff0c;Viewport 的大小会有所不…...

PWM 模式

一、介绍 PWM&#xff08;脉宽调制&#xff0c;Pulse-width modulation&#xff09;是一种通过调节脉冲信号的宽度来控制电能输出的方法。PWM是一种方波信号&#xff0c;通常在电子和电气工程中用于调节功率输送&#xff0c;控制电机速度&#xff0c;调节LED亮度&#xff0c;以…...

模拟算法(3)_Z字形变换

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 模拟算法(3)_Z字形变换 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题目链…...

Go语言实现长连接并发框架 - 任务执行流路由模块

文章目录 前言接口结构体接口实现项目地址最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;上篇博客中我们实现了任务执行流上下文部分&#xff0c;接下来我们实现一下任务执行流的路由模块&#xff0c;基于该模块可以实现将消息转发到相应注册的任务执行流中进行处理 接…...

Windows 编译 FFmpeg 源码详细教程

FFmpeg FFmpeg 是一个开源的多媒体框架,它包括了一整套工具和库,可以用来处理(转码、转换、录制、流式传输等)音频和视频。FFmpeg 支持广泛的音视频格式,并且可以在多种操作系统上运行,包括 Windows、Linux 和 macOS。 FFmpeg 的主要组件包括: ffmpeg:这是一个命令行工…...

JavaCV 实现视频链接截取封面工具

引入必要依赖 <!--JavaCV--> <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.7</version> </dependency> <dependency><groupId>cn.hutool</groupI…...

初识Linux · 进程替换

目录 前言&#xff1a; 1 直接看代码和现象 2 解释原理 3 将代码改成多进程版本 4 认识所有函数并使用 前言&#xff1a; 由前面的章节学习&#xff0c;我们已经了解了进程状态&#xff0c;进程终止以及进程等待&#xff0c;今天&#xff0c;我们学习进程替换。进程替换我…...

项目-坦克大战学习-人机ai

我们要知道&#xff0c;人机的移动和玩家的移动方式是一样的&#xff0c;所以我们可以将玩家移动代码以及检测碰撞代码移过来&#xff0c;唯一不同的就是人机检测到碰撞后会改变方向继续移动而不是停止 所以我们需要一个随机数使人机检测到碰撞后随机修改方向 Random rd new …...

YOLOv11改进 | Conv篇 | YOLOv11引入SKConv

1. SKConv介绍 1.1 摘要:在标准卷积神经网络(CNN)中,每层中阿尔蒂神经元的感受野被设计为共享相同的大小。在神经科学界众所周知,视觉皮层神经元的感受野大小受到刺激的调制,这在构建CNN时很少考虑。我们在CNN中提出了一种动态选择机制,允许每个神经元根据输入信息的多…...

招联2025校招内推

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...