<C++>深度学习继承

news2025/1/12 16:02:02

目录

一、继承概念

二、继承的语法

2.1继承关系和访问限定符

2.2派生类继承基类后的成员权限

三、基类和派生类的对象赋值转换

四、继承中的作用域

五、派生类的默认成员函数

六、继承与友元

七、继承与静态成员        

八、复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

菱形继承:菱形继承是多继承的一种特殊情况。

虚拟继承解决数据冗余和二义性的原理

九、继承的总结

十、继承的一些面试题

 1. 什么是菱形继承?菱形继承的问题是什么?

2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的

3. 继承和组合的区别?什么时候用继承?什么时候用组合?


一、继承概念

继承是面向对象程序设计使代码复用的重要手段,继承中有父类(基类)和子类(派生类);

继承中派生类可以使用父类的成员函数和成员变量;

class A // 基类、父类
{
public:
	int _a;
};

class B : public A // 派生类、子类
{
private:
	int _b;
};

二、继承的语法

下面我们看到的Student是派生类(子类),Person是基类(父类)

2.1继承关系和访问限定符

继承关系和访问限定符是类似的;

2.2派生类继承基类后的成员权限

类成员/继承方式public继承protected继承private继承
基类中的public成员派生类中的public成员派生类中的protected成员派生类中的private成员
基类中的protected成员派生类中的protected成员派生类中的protected成员派生类中的privete成员
基类中的private成员派生类中不可见派生类中不可见派生类中不可见

 虽然有这么多的继承方法组合,但是我们最常使用的只有public继承、public成员、protected成员组合的方式继承;

私有成员的意义?

不想被子类继承的成员,可以设计成私有;

 保护成员的的意义?

基类中想给子类复用,但是又不想直接暴露访问成员,就该定义成保护;

注意:

struct默认继承/访问限定符 : public

class默认继承/访问限定符 :private

总结:

  1.  基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它。
  2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
  3.  实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
  5.  在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强

三、基类和派生类的对象赋值转换

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 或者切割。寓意把派生类中父类那部分切来赋值过去。
  • 基类对象不能赋值给派生类对象。
  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类 的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。
class Person
{
public:
	Person(string name = "zs", int age = 0)
		:_name(name)
		,_age(age)
	{
		
	}

	void Print()
	{
		cout << _name << endl;
		cout << _age << endl;
	}

public:
	string _name;
	int _age;
};

class Student : public Person
{
public:
	Student(int No = 213310)
		:_No(No)
	{
		
	}

	void Print()
	{
		cout << _name << endl;
		cout << _age << endl;
		cout << _No << endl;
	}
private:
	int _No; //编号
};

int main()
{
	Person p1;
	Student s1;

	s1.Print();

	p1._name = "张三";//修改基类数值
	p1._age = 18;

	p1 = s1; // 将子类赋值给父类
	p1.Print();

    //结果两次打印的结果不变

	return 0;
}

 上面的运行结果子类已将将父类重新赋值;

四、继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。

2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏

4. 注意在实际中在继承体系里面最好不要定义同名的成员。

class A
{
public:
	void Print() { cout << "class A" << endl; }
public:
	int _a = 1;
};

class B : public A
{
public:
	void Print(){	cout << "class B" << endl;	}
public:
	int _b = 2;
};

class C : public A
{
public:
	void Print() { A::Print(); }
public:
	int _c = 2;
};

int main()
{
	B b;
	b.Print(); // 调用b中重定义的Print()

	C c;
	c.Print(); // 通过c显示调用A中的Print()

	return 0;
}

 

五、派生类的默认成员函数

上面的图我们可以从下面的几点去理解: 

1.派生类的默认构造函数会调用基类的构造函数,如果基类没有构造函数,那么必须在派生类的的初始化列表阶段显示调用;

2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的初始化;

3.派生类的operator=必须要调用基类的operator=完成基类的复制;

4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能 保证派生类对象先清理派生类成员再清理基类成员的顺序。

5. 派生类对象初始化先调用基类构造再调派生类构造;

6. 派生类对象析构清理先调用派生类析构再调基类的析构;

7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加 virtual的情况下,子类析构函数和父类析构函数构成隐藏关系;

六、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

七、继承与静态成员        

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

八、复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

class A
{
public:
	void Print() { cout << "class A" << endl; }
public:
	int _a = 1;
};

class B : public A // 只继承了A类
{
public:
	void Print(){	cout << "class B" << endl;	}
public:
	int _b = 2;
};

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

class A
{
public:
	void Print() { cout << "class A" << endl; }
public:
	int _a = 1;
};

class B
{
public:
	void Print() { cout << "class B" << endl; }
public:
	int _b = 2;
};

class C : public A, public B // 继承了A类和B类
{
public:
	void Print() { A::Print(); }
public:
	int _c = 2;
};

菱形继承:菱形继承是多继承的一种特殊情况。

 


class A
{
public:
	void Print() { cout << "class A" << endl; }
public:
	int _a = 1;
};

class B : public A
{
public:
	void Print() { cout << "class B" << endl; }
public:
	int _b = 2;
};

class C : public A
{
public:
	void Print() { A::Print(); }
public:
	int _c = 3;
};
class D : public B, public C
{
public:
	void Print() { A::Print(); }
public:
	int _d = 4;
};

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。 在Assistant的对象中Person成员会有两份。

 

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和 Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地 方去使用。虚继承即在继承方式前加virtual关键字

虚拟继承解决数据冗余和二义性的原理

使用了virtual虚拟继承后,派生类会将基类的成员放到一个公共的区域,所有的派生类共享这个区域,会产生一个指针指向一个虚表,虚表中存放各个派生类相对于公共区域的偏移量,当派生类需要访问基类的成员时就需要处理一下偏移量,找到基类的成员,再进行访问;

 

九、继承的总结

  • 一定不要设计出多继承
  • 多继承可以说是C++的缺陷之一
  • 继承和组合

public继承是一种is-a的关系,也就是说每一个派生类对象都是一个基类;

组合是一种has-a的关系,假设B组合了A,每一个对象中都有一个A对象;

  • 优先使用对象组合,而不是继承
  • 继承允许你根据基类的实现来定义派生类的实现
  • 继承一定程度破坏了类的封装,基类的改变对派生类的影响很大
  • 对象组合和基类间的依赖关系很强,耦合度高
  • 对象组合是类继承之外的另外一种复用选择
  • 实际尽量多去使用组合

十、继承的一些面试题

 1. 什么是菱形继承?菱形继承的问题是什么?

菱形继承:多继承的一种特殊,派生类A和派生类B继承了一个基类,同时派生类C又多继承了A和B就构成了菱形继承

解决方法:使用virtual虚拟继承

2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的

菱形虚拟继承:在继承方式前面加vritual关键字

原理:加了virtual后,派生类将基类继承的成员放在一个公共的区域,同时多创建了一个指针指向一个虚表,虚表中存放该派生类相对于基类成员的偏移量,在派生类访问基类时再进行偏移量的处理

3. 继承和组合的区别?什么时候用继承?什么时候用组合?

继承:is-a也就是说每一个派生类对象都是一个基类;

组合:has-a假设B组合了A,每一个对象中都有一个A对象;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CS下载、安装以及简单使用

Cobalt Strike4.0中文版下载 渗透利器CobaltStrike 4.0 免费汉化版(含原版补丁) 下载-脚本之家 (jb51.net)下载解压&#xff0c;有中文版本和原版 server&#xff1a;kali&#xff08;自带Java环境&#xff09; clinet&#xff1a;win10 这里直接把原版拖进kali运行不了&#x…

买卖股票的最好时机(一、二)

目录 买卖股票的最好时机(一) 动态规划解决 贪心思想解决 买卖股票的最好时机(二) 贪心思想解决 动态规划解决 买卖股票是经典的动态规划问题&#xff0c;在动态规划的学习与练习中&#xff0c;最令我煎熬的就是状态方程递推&#xff0c;经常出现状态定义好后&#xff0c…

【毕业设计】10-基于单片机的车站安检门/磁性霍尔传感器系统设计(原理图+源码+仿真工程+论文)

【毕业设计】10-基于单片机的车站安检门/磁性霍尔传感器系统设计&#xff08;原理图源码仿真工程论文&#xff09; 文章目录【毕业设计】10-基于单片机的车站安检门/磁性霍尔传感器系统设计&#xff08;原理图源码仿真工程论文&#xff09;资料下载链接任务书设计说明书摘要设计…

css的三种引入方式

目录 三种引入方式 1.行内样式 2.内部样式(内嵌式) 3.外部样式 3.1外链式 3.2导入式 引入方式的优先级 三种引入方式 1.行内样式 行内样式就是直接把css样式添加在HTML标签中,作为style样式的属性值 <!-- 行内/内嵌/嵌入式/内联样式 --><!-- 背景颜色 backgr…

动力节点索引优化解决方案学习笔记——查询优化

3.查询优化 创建一张测试表并插入数据&#xff1a; drop table if exists students; CREATE TABLE students (id INT PRIMARY KEY AUTO_INCREMENT COMMENT "主键id",sname VARCHAR (24) COMMENT 学生姓名,age INT COMMENT 年龄,score INT COMMENT 分数,time TIMEST…

微服务与中间件系列——GateWay整合Swagger3增强Knife4j

微服务与中间件系列——GateWay整合Swagger3增强Knife4jGateWay整合Swagger3增强Knife4j&#xff08;easy模式&#xff09;目的服务端1.导入依赖2.编写配置类3.yaml配置GateWay网关1.文档枚举2.SwaggerProvider3.yaml配置结果增强版服务端1.增加配置参数类2.修改配置类3.修改ya…

Spring框架一文带你吃透IOC技术

本文目录 文章目录本文目录✨loC的技术实现✨实现步骤✨创建接口和实现类✨创建Spring的配置文件和声明bean✨创建spring容器对象✨spring容器创建对象的特点✨创建非自定义类的对象✨创建没有接口的类的对象✨获取容器中对象的信息loC&#xff0c;Inversion ofControl:控制反转…

肿瘤NGS测序行业背景介绍

肿瘤NGS测序行业背景介绍1、行业基本情况1.1 行业分类1.2 主管部门1.3 行业监管1.4 行业法规1.5 医保报销流程2、肿瘤基本介绍2.1 肿瘤基础概念2.2 癌症分期2.3 致癌风险2.4 肿瘤早期检测3、分子诊断在肿瘤早检的场景3.1 传统肿瘤早期检测方式3.2 分子诊断3.3 技术性能评价指标…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.9 MongoDB 下载与安装

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.9 MongoDB 下载与安装4.9.1 MongoDB 下载4.9.2 安…

PLATFORMIO学习文档之esp8266 驱动SSD1306 128x64OLED显示屏示例

写文章也是为了记录自己的学习过程&#xff0c;顺便给后来人指指路&#xff0c;迷路了可别怪我~~~~~~ 开发环境&#xff1a;PLATFORMIO 1、首先上一张我用的ESP8266开发板ESP-12E 首先新建一个工程&#xff0c;输入项目名称&#xff0c;选择自己所用的开发板&#xff0c; …

灵界的科学丨二、耳朵及手指识字的实验启示

摘自李嗣涔教授《灵界的科学》 如果有一天&#xff0c; 能教会盲人用手指或耳朵来「看」&#xff0c; 用触觉来取代眼睛&#xff0c; 人类社会中&#xff0c;将不再存在「盲」这种残疾。 发现人体潜能――耳朵与手指识字 眼睛可以观赏五彩缤纷的世界&#xff0c;耳朵可以听…

Joe 主题 Halo 移植版搭建部署教程

搭建成功之后的效果图如下 Halo安装部署 Docker 安装 Halo docker pull halohub/halo:1.5.4 # docker 从远程仓库下载镜像 docker run -it -d --name fl1906 -p 666:8090 -v ~/fl1906:/root/.halo halohub/halo:1.5.4 --restartalwaysdocker run -it -d --name walktop -p 7…

【算法系列】非线性最小二乘-列文伯格马夸尔和狗腿算法

系列文章目录 【算法系列】卡尔曼滤波算法 【算法系列】非线性最小二乘求解-直接求解法 【算法系列】非线性最小二乘求解-梯度下降法 【算法系列】非线性最小二乘-高斯牛顿法 【算法系列】非线性最小二乘-列文伯格马夸尔和狗腿算法 文章目录 系列文章 文章目录 前言 …

期末网页设计作业素材 (民宿 5页 带地图)

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 游景点介绍 | 旅游风景区 | 家乡介绍 | 等网站的设计与制作 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 CSS&…

JSON解析看这一篇就够了

目录 文章目录[toc]一、JSON是什么 &#xff08;&#xffe3;︶&#xffe3;&#xff09;↗总而言之&#xff1a;二、为什么要用JSON三、不同的JSON解析方式3.1 gson和[fastjson](https://so.csdn.net/so/search?qfastjson&spm1001.2101.3001.7020)简而言之3.2 gson案例3.…

15.Servlet

目录 1.Servlet的日常开发 1.WebServlet(...此处填写资源路径) 1.那么什么时候Tomcat会启动失败&#xff1f; 2.响应体是文本内容&&字符集编码是uft-8 3.读取请求参数 4.资源的重定向&#xff08;redirect&#xff09; 2.面试问题&#xff1a;GET和POST有什么区…

代码随想录算法训练营三期 day 23 - 二叉树(9)

669. 修剪二叉搜索树 原文链接&#xff1a;669. 修剪二叉搜索树 题目链接&#xff1a;669. 修剪二叉搜索树 视频链接&#xff1a;669. 修剪二叉搜索树 在上图中我们发现结点 000 并不符合区间要求&#xff0c;那么将结点 000 的右孩子 结点 222 直接赋给 结点 333 的左孩子就…

Java设计模式 (1) —— Iterator迭代器模式

拿到这个话题&#xff0c;首先搞清楚&#xff1a;什么是迭代器&#xff1f; 大家都学过 for-i 循环&#xff0c;for-i循环的本质就是 通过i 或者 i-- &#xff0c;以实现 从数据的一端 一个一个地遍历数据元素&#xff0c;直到另一端的最后一个元素&#xff0c;将这里 i 的通用…

【Linux】基础IO —— 深入理解文件系统 | 软硬链接

&#x1f308;欢迎来到Linux专栏~~ 深入理解文件系统 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的…

安全需求和挑战

安全需求和挑战 从风险管理的角度讲&#xff0c;主要就是管理资产、威胁、脆弱性 和防护措施及其相关关系&#xff0c;最终保障云计算平台的持续安全&#xff0c;以及 其所支撑的业务的安全。 云计算平台是在传统 IT技术的基础上&#xff0c;增加了一个虚拟化 层&#xff0c;并…