继承

news2024/12/23 12:55:48

目录

引入

继承介绍

概念

优点

分类

公有继承

保护继承

私有继承

特点

单继承

多继承

赋值

介绍

分类

对象之间赋值(拷贝构造)

验证普通赋值需要创建临时变量

指针/引用赋值

赋值原理

继承中的作用域

介绍

隐藏 / 重定义

前提

介绍

派生类的默认成员函数

构造

显式构造父类

拷贝构造

operator=重载

析构

特殊成员的继承

友元

静态成员

菱形继承

引入

介绍(存在的问题)

虚拟继承

介绍

原理 

不使用virtual时

加入了virtual关键字后

使用场景


引入

c++是面向对象的语言,那么不同对象是不是有可能拥有相同特征呢?

那这个相同特征也算是一种对象(比较宽泛定义的对象),那就也得写一个类(假设为A),来囊括它的行为

拥有相同特征的两个对象就都需要有A(基类),有多种方法可以实现它:

  • 内部类(基类在子类内部定义)
  • 直接实例化(基类在子类外部定义)
  • 继承(复用基类的代码)

各有各的优缺点,具体使用什么可能看情况吧,目前还不清楚(跪),反正这里就只讲继承噜

继承介绍

概念

是面向对象程序设计使代码可以复用的最重要的手段,它允许一个类(称为子类或派生类)继承另一个类(称为父类、基类或超类)的属性和方法

继承使得子类能够拥有父类的特性,同时也可以在此基础上添加自己的特性或修改继承来的特性

 

优点

分类

公有继承

  • 子类从父类继承的成员在子类中的访问权限不受限制,父类成员的权限在子类中不变,保留了接口的一致性
  • 是最常用最常用的继承方式

保护继承

  • 子类从父类继承的成员在子类中的访问权限变为保护(protected)
  • 子类可以访问父类的保护成员,但外部无法访问子类从父类继承的保护成员
  • 保护成员限定符是因继承才出现的 (因为平时的时候,私有和保护的权限是一样的,但在继承父类的成员时就有所差异)

私有继承

  • 子类从父类继承的成员在子类中的访问权限变为私有(private)
  • 这意味着子类可以访问父类的成员,但外部无法访问子类从父类继承的成员

特点

  • 父类的私有成员可以被继承,但处于"不可见"状态,子类和子类外部都无法访问,但可以通过父类的成员函数间接访问
  • 父类成员的权限 和 子类继承父类的方式,取其权限小的作为父类成员在子类中的权限
  • 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public

单继承

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

 

多继承

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

多个基类之间用逗号隔开 

 

赋值

介绍

一般来说,不同类之间无法赋值,但继承不一样,父类和子类可以赋值,但只局限于子类赋给父类

子类的对象可以赋值给父类的对象/指针/引用

分类

对象之间赋值(拷贝构造)

  • 上面的结果很奇怪,只有一次构造,但却有两次析构
  • 看过发现是tmp也析构了,但奇怪的是,tmp被创建的时候并没有走我写的构造函数
  • 于是我查了查,加上了拷贝构造,结果就对了
  • class person {
    public:
    	person() {
    		cout << "person1" << endl;
    		_a = 1;
    	}
    	person(person& tmp) {
    	    _a = tmp._a;
    		cout << "person2" << endl;
    	}
    	void work() {
    		cout << "person::work()" << endl;
    	}
    	~person() {
    		cout << "~person" << endl;
    		_a = 0;
    	}
    public:
    	int _a;
    };
    class student :public person {
    public:
    	void work() {
    		cout << "person::work()" << endl;
    	}
    public:
    	int _tmp = 0;
    };
    int main() {
    	student s;
    	s._a = 2;
    	person tmp = s;
    	cout << tmp._a << endl;
    	return 0;
    }

  • 结论是:赋值转换的过程中,应该是先进行隐式类型转换,将子类看作父类(抛弃掉子类多出的部分),然后调用子类的拷贝构造,进行数值拷贝

     

验证普通赋值需要创建临时变量

不同的普通类型之间的赋值是 -- 先创建一个临时变量,然后再赋值  

int a=0;
double& b=a;
  • 这样的代码无法编过,但在double前+const就行,原因是:
  • 编译器会在赋值前先创建一个临时变量tmp,临时变量具有常性,所以它是const double类型,需要和const double&类型绑定

指针/引用赋值

同理,指针/引用也是可以赋值的,也同样是用子类的指针/对象赋值给父类的指针/引用

  • 这样父类的那个指针/引用的范围只是子类的一部分
  • 其中,父类指针可以通过强制转换类型来赋值给子类指针,但可能会越界

赋值原理

直接切割

  • 如果是少到多赋值,那子类多出来的成员变量该怎么办捏,所以采用多到少赋值

继承中的作用域

介绍

每一个类都有独立的作用域,所以父类和子类也都拥有自己的作用域

作用域的本质 -- 限定编译器查找的范围

隐藏 / 重定义

前提

c++允许父类和子类拥有同名成员

其中,函数只要同名就可以达成隐藏的条件,而不用考虑参数列表

注意:父类和子类中的同名函数不构成重载(重载要在同一作用域的啊摔)

介绍

当子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问

  • 意思就是,当出现命名冲突,优先使用子类中的成员
  • 如果是函数的话,即使子类中的函数不符合调用时的参数,也不会去父类找,而是直接报错
  • 可以想成:编译器很懒,只要按照查找顺序找到名字一样的,就到此为止,它不管是否符合语法

 在实际中,最好不要定义同名的成员,没意义,而且看的不清楚

派生类的默认成员函数

在使用过程中,将父类看作一个对象

构造

  • 父类没有默认构造,而子类也没有为父类显式构造,就会出错(编译器不会为类中的自定义类型生成默认构造)

显式构造父类

像定义一个匿名对象一样,传入参数即可

  • 父类有默认构造,子类没有默认构造时,系统会自动生成子类的默认构造,它会调用父类默认构造(按照基类声明顺序调用,因为可能有多继承),但对其他成员不做初始化
  • 按照声明的顺序的话,类的开头肯定是父类(类名那里有写对父类的继承方式),然后才是自己

拷贝构造

  • c++规定,子类的拷贝构造必须显式调用父类的拷贝构造,而不能在子类中完成对父类成员的初始化
  • 写法和上面的图里一样
  • 这里的person的拷贝构造,参数应该是person类型,但是可以用student的直接传,是因为 -- 子类对象可以赋值给父类对象/引用(前面的知识堂堂登场噜) 

operator=重载

和拷贝构造类似,必须要显式调用基类的operator=

  • 而且要注意,不能直接这么写,父类和子类的operator=会构成隐藏,所以这里应该使用类域来指明是哪个operator=(不然堆栈就爆了,疯狂自己调用自己)

析构

  • 前面的构造有说明,先是父类构造,再是子类
  • 所以按照堆栈建立顺序,应该是先析构子类,再析构父类
  • 除此之外,因为子类可以用父类成员,那么如果先析构父类,当子类析构中出现析构完父类后还要使用父类成员这种歹毒的情况就完蛋
  • 所以,综合来看,必须得先析构子类
  • 而且父类的析构是可以不用显式调用嘟,编译器会在子类析构结束时自动调用父类析构

特殊成员的继承

友元

友元关系是无法继承的,也就是基类的友元函数无法访问子类的保护/私有成员

如果需要使用,需要在子类也声明一下这个友元关系

静态成员

  • 在类和对象中,我们知道静态成员是属于类的,而不是每个对象
  • 所以在继承中,静态成员也很特殊,它只被子类继承了使用权
  • 无论有多少个子类,他们之中都只有一份静态成员,就像所有被实例化出的对象中,也只有一份

菱形继承

引入

前面提到,继承有单继承和多继承,而菱形继承就是其中的一个特殊情况

其形状也不一定是菱形,只要最终有交汇,就属于这样的情况

介绍(存在的问题)

  • 像这样的,一个子类继承的两个基类中,都继承了同一种基类
  • 就会导致一个assistant中有两个person
  • 但很显然,作为面向对象的语言,一个对象不可能存在两种作为person的状态(比如person里会有的名字,性别啥的),所以出现了数据冗余(这个还真不好解决)
  • 而且调用的时候,因为有两份person,就会出现二义性,需要指明类域(有点麻烦的捏)

为了解决这个问题,c++提出了虚拟继承这一概念 

虚拟继承

介绍

是一种用于处理多重继承中的二义性和问题的技术,它允许你在继承关系中使用虚拟基类,从而解决由于多个派生类共享同一个基类实例而导致的问题

通过在继承时使用virtual关键字来解决这个问题

当一个类通过 虚拟继承 继承一个基类时,无论多少个派生类都会共享同一个基类实例

 

可以看到,d中的 B类和C类的 A类中的_a是共享的

原理 

不使用virtual时

class A
{
public:
	int _a;
};

class B : public A
{
public:
	int _b;
};
class C : public A
{
public:
	int _c;
};

class D : public B, public C
{
public:
	int _d;
};

可以看出来,是很规整的按照顺序存放在内存中,且都有一份A

 

加入了virtual关键字后

class A
{
public:
	int _a;
};

class B : virtual public A
{
public:
	int _b;
};
class C : virtual public A
{
public:
	int _c;
};

class D : public B, public C
{
public:
	int _d;
};

会发现,在原先A所在位置,被一段看起来很像地址的数字代替了

如果在内存中查找(注意普通电脑一般是小端存储哦,数字倒过来才是地址)

(这是重新生成了的)

跳转过去后:

 

第一行是0,第二行是20(16进制)

C中的地址跳转过去后: 

第一行仍然是0,第二行是12

  • 20和12都是4的倍数,而内存中不会莫名其妙存放这么个数字,它肯定和A类有关联
  • 所以,真相只有一个 -- 存储的数字代表距离A类地址的字节数
  • 而且,由于D的数据结构已经确定,那么那份偏移量表可以被多个实例化后的D对象共用(因为里面是偏移量)

但其实,对于d来说,找_a其实不需要用到偏移量,因为他知道A被自己放在最后,直接拿就行

使用场景

需要切割时,偏移量会起到大大的作用

  • 当pb指向d中的B类时,B类既有自己的成员,也有A的成员
  • 可是A被放在了d的底层,和B隔开了
  • 这里就需要偏移量,来为B类找A使用

一旦使用virtual,单独实例化B/C时,它里面的构造也会改变成使用偏移量表的形式

 

  • 那么,当我们拿取A中成员时,无论是谁在拿,在汇编上都是一样的
  • 拿到偏移量,然后访问
  • 所以,它统一了子类的访问方法
  • 且证明了在切割时 不需要处理原对象,直接按照规定方法拿就行

 

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

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

相关文章

TIA博途_更新或修改程序时,如何避免数据块中的参数丢失?

TIA博途_更新或修改程序时,如何避免数据块中的参数丢失? DB 快照功能 可以通过捕获 DB 块变量实际值快照用于恢复值操作,捕获的实际快照值可以复制到 CPU 中的实际值中,也可以用于替换变量的起始值。 通过快照能解决以下场景的问题: • 在 HMI 中设置了很多工艺参数,担心…

python怎么提取视频中的音频

目录 操作步骤 1. 安装MoviePy库&#xff1a; 2. 导入MoviePy库和所需的模块&#xff1a; 3. 提取音频&#xff1a; 可能遇到的问题 1. 编解码器支持&#xff1a; 2. 依赖项安装&#xff1a; 3. 文件路径问题&#xff1a; 4. 内存消耗&#xff1a; 5. 输出文件大小&a…

Rabbitmq的消息转换器

Spring会把你发送的消息序列化为字节发送给MQ&#xff0c;接收消息的时候&#xff0c;还会把字节反序列化为Java对象 ,只不过&#xff0c;默认情况下Spring采用的序列化方式是JDK序列化。众所周知&#xff0c;JDK序列化存在下列问题&#xff1a; 数据体积过大 有安全漏洞 可读…

水稻叶病害数据集(目标检测,yolo使用)

1.数据集文件夹 train文件夹&#xff08;44229张&#xff09;&#xff0c;test文件夹&#xff08;4741张&#xff09;&#xff0c;valid文件夹&#xff08;6000张&#xff09; 2.train文件夹展示 labels展示 标签txt展示 data.yaml文件展示 对数据集感兴趣的可以关注最后一行…

vue cli构建的项目出现 Uncaught runtime errors

使用 vue/cli 脚手架构建的项目&#xff0c;在 npm run dev 运行后&#xff0c;页面出现 Uncaught runtime errors 报错遮罩层&#xff0c;如下图所示。 报错原因 这种错误通常是运行时出的问题&#xff0c;可能是网络错误&#xff0c;可能是变量未定义等等。 这种错误默认在开…

华硕笔记本摄像头倒置怎么办?华硕笔记本摄像头上下颠倒怎么调整

笔记本电脑相较于台式电脑&#xff0c;更易携带&#xff0c;解决了很大一部分人的使用需求。但是笔记本电脑也存在很多不足&#xff0c;比如华硕笔记本电脑就经常会出现摄像头倒置的错误&#xff0c;出现这种问题要如何修复呢&#xff1f;下面就来看看详细的调整方法。 华硕笔记…

linux的make和makefile学习

linux的make和makefile学习 准备工作使用GNU链接库链接到math库编写复利程序 创建自己的库链接到主目录 不同的C标准 准备工作 安装GCC和Make工具 安装中文输入法 参考&#xff1a;http://t.csdn.cn/eH0Ow sudo apt-get update sudo apt-get install ibus sudo apt-get inst…

Flutter Web 项目网络请求报 XMLHttpRequest error 解决方案

使用http库进行简单的网络请求时&#xff0c;运行在Chrome浏览器上&#xff0c;网络请求一直报错 XMLHttpRequest error&#xff0c;而在iOS 模拟器上运行则正常&#xff0c;后面在postman上发送请求&#xff0c;也是正常的。这就是很尴尬了&#xff01;&#xff01;&#xff0…

JavaScript基础知识1

基本说明 1.JavaScript能改变html内容&#xff0c;能改变html属性&#xff0c;能改变html样式&#xff08;CSS&#xff09;&#xff0c;能完成页面的数据验证。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><…

深入理解 JVM 之——动手编译 JDK

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 本篇为深入理解 Java 虚拟机第一章的实战内容&#xff0c;推荐在学习前先掌握基础的 Linux 操作、编译原理基础以及扎实的 C/C 功底。 该系列的 GitHub 仓库&#xff1a;https://github.com/Doge2077/lear…

iOS开发Swift-3-UI与按钮Button-摇骰子App

1.创建新项目Dice 2.图标 删去AppIcon&#xff0c;将解压后的AppIcon.appiconset文件拖入Assets包。 3.将素材点数1-6通过网页制作成2x&#xff0c;3x版本并拖入Asset。 4.设置对应的UI。 5.拖入Button组件并设置style。 6.Ctrl加拖拽将Button拖拽到ViewController里&#xff0…

MySQL8.0.22安装过程记录(个人笔记)

1.点击下载MySQL 2.解压到本地磁盘&#xff08;注意路径中不要有中文&#xff09; 3.在解压目录创建my.ini文件 文件内容为 [mysql] # 设置mysql客户端默认字符集 default-character-setutf8[mysqld] # 设置端口 port 3306 # 设计mysql的安装路径 basedirE:\01.app\05.Tool…

动手学深度学习(四)多层感知机

经过了多层感知机后&#xff0c;相当于将原始的特征转化成了新的特征&#xff0c;或者说提炼出更合适的特征&#xff0c;这就是隐藏层的作用。 from&#xff1a;清晰理解多层感知机和反向传播 - 知乎 一、多层感知机的从零开始实现 import torch from torch import nn from d2…

阿里云大数据实战记录8:拆开 json 的每一个元素,一行一个

目录 一、前言二、目标介绍三、使用 pgsql 实现3.1 拆分 content 字段3.2 拆分 level 字段3.3 拼接两个拆分结果 四、使用 ODPS SQL 实现4.1 拆分 content 字段4.2 拆分 level 字段4.3 合并拆分 五、使用 MySQL 实现六、总结 一、前言 商业场景中&#xff0c;经常会出现新的业…

h5分享页适配手机电脑

实现思路 通过media媒体查询结合rem继承html文字大小来实现。 快捷插件配置 这里使用了VSCode的px to rem插件。 先在插件市场搜索cssrem下载插件&#xff1b; 配置插件 页面编写流程及适配详情 配置meta h5常用配置信息:<meta name"viewport" content&quo…

音频基本知识

声音传播方式: 1)声音的传播需要介质,在真空中不能传播; 2)声波属于纵波,即如下图传播方向与振动方向一致; 声音速度: 1)常温常压下,一般空气速度为340m/s; 2)温度越高,声速越大; 3)液体、固体的传播速度比空气快; 人耳可接收到的频域范围: 1)通常范围…

【DevOps视频笔记】8. Jenkins 配置

一、Jenkins 入门配置 1. 工具 / 插件 介绍 二、插件和工具配置 1. 配置 JDK 和 Maven Stage 1&#xff1a;将服务器中 JDK 和 Maven 映射到 jenkins 容器中 Stage 2&#xff1a;jenkins 全局配置中 -- 指定JAVA_HOME目录 Stage 3&#xff1a;jenkins 全局配置中 -- 指定…

【SQL】1731. 每位经理的下属员工数量 ( 新思想:确定左表,依次添加后续字段)

leetcode题目链接 注意点 确定左表&#xff08;即&#xff0c;确定result表中的主键)&#xff0c;依次添加后续字段。注意&#xff1a;主键可能是一个字段&#xff0c;也可能是多个字段COUNT(DISTINCT())&#xff0c;一般为了防止重复&#xff0c;使用COUNT计数时&#xff0c…

Spring——Spring基础

文章目录 1. Spring架构2. RestController vs Controller3. Autowired和Resource的区别是啥4. Spring IOC & AOP4.1 谈谈自己对于 Spring IoC 和 AOP 的理解IoCAOP 4.2 Spring AOP 和 AspectJ AOP 有什么区别&#xff1f; 5. Spring bean5.1 Spring 中的 bean 的作用域有哪…

springboot添加SSL证书,支持https与http

文章目录 一、添加ssl证书二、配置文件三、配置同时支持HTTPS与HTTP四、启动 一、添加ssl证书 将证书文件放在/resource目录下 二、配置文件 修改配置文件 server:ssl:# 指定保存SSL证书的秘钥存储的路径key-store: classpath:dev.cobona.cn.pfx# 访问秘钥存储的密码key-store-…