继承、多继承

news2024/11/18 15:51:13

继承

image-20230308213700819

文章目录

  • 继承
    • 继承的概念
    • 继承的定义
        • 继承方式和访问限定符
          • 继承基类成员访问方式的变化
    • 基类和派生类对象赋值转换
    • 继承中的作用域
    • 派生类的默认成员函数
      • 构造函数
      • 拷贝构造
      • 赋值重载
      • 析构函数
      • 继承和友元
      • 继承和静态成员
      • 多继承,菱形继承和菱形虚拟继承
        • 单继承:一个子类只有一个直接父类
        • 多继承:一个子类有两个或两个以上的直接父类
        • 多继承的一种情况:菱形继承
        • 虚拟菱形继承
      • 继承与组合

继承的概念

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

继承的定义

image-20230114165416378

实例代码,写了一个Person的类,然后一个Teacher和一个Student的类通过public继承它

class Person
{
public:
	void Print()
	{
		cout << "name: " << _name << endl;
		cout << "age: " << _age << endl;
	}
private:
	string _name = "pjl";
	int _age = 18;
};
class Student:public Person
{
protected:
	int studnum;//学号
};

class Teacher:public Person
{
protected:
	int _teachnum;//教师学号
};

int main()
{
	Student st;
	Teacher te;
	st.Print();
	te.Print();
	return 0;
}

通过调试可以看到子类Student、Teacher可以继承到父类Person的public成员,并且在对象里面有父类的private成员

image-20230114170047528

image-20230114170504760

那么具体的继承方式是怎么样的呢?

继承方式和访问限定符

image-20230114171102999

image-20230114171159252

继承基类成员访问方式的变化
类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

咋理解呢?

1.基类的public成员,派生类public继承那么就是public成员,protected继承就是protected成员,private继承就是private成员,以此类推。即取基类的成员访问限定符和派生类继承方式取小的那一个。

2.但是一个特殊的地方:基类的private成员,在派生类是不可见的,即基类的private成员还是被继承到了派生类对象中,但语法上限制派生类对象无论在类中还是类外都不能去访问它! 如果基类想要自己的private成员不在类外被访问,但可以在派生类中访问就定义成员为protected。这也可以看出保护成员限定符是因继承才出现的。

3.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

image-20230114182619262

基类和派生类对象赋值转换

1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去

image-20230114190333313

image-20230114190742010

image-20230114190909622

继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

可以看到这里类B的fun函数是带参的,那么调用带参的自然就是类B的,那如果直接调用不带参的呢?会不会直接是类A的呢?

image-20230114192915528

可以看到报错了,类B和类A的fun函数函数名相同就构成隐藏/重定义,所以需要显示访问!

image-20230114192948402

image-20230114193016057

派生类的默认成员函数

构造函数

派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。【等同于在初始化列表把传的参数传给基类拿去初始化】

如果没有写初始化列表,那么构造出来的对象关于基类那部分的值由基类的缺省参数决定,反之由主函数传的参数决定

我写了一个Person类和Student类,Person类是Student的父类;里面都有构造、拷贝构造、赋值重载、析构函数。

然后一个简单的调用子类。

image-20230114211841436

现在我把子类构造函数的初始化列表注释掉,可以看到子类构造函数调用了父类的构造函数。

image-20230114211645334

如果基类没有默认的构造函数(基类自身没有写构造函数,编译器才会产生默认构造函数),派生类构造函数的初始化列表阶段显示调用。

image-20230114214136942

拷贝构造

派生类继承基类的那部分成员必须要必须调用基类的拷贝构造完成基类的拷贝初始化,其余的部分调用派生类的拷贝构造即可。

如果没有写初始化列表,那么拷贝构造出来的对象关于基类那部分的值由基类的缺省参数决定,反之由主函数传的参数决定

image-20230114215129022

赋值重载

派生类继承基类那部分成员必须必须要调用基类的operator=完成基类的赋值,否则派生类会自己赋值给自己。其他部分调用派生类自身的赋值重载。

image-20230114215600171

析构函数

1.派生类析构函数和基类析构函数构成隐藏(由于多态关系需求,所有析构函数都会被特殊处理成destructor函数名)

2.析构顺序为派生类先析构,基类后析构。派生类析构不需要调用基类析构函数,派生类析构完会自动调用基类析构

若在派生类显示调用基类析构函数呢?

image-20230307162409777

可以看到基类析构函数被调用了两次,若基类中有使用到空间资源,那么那部分空间会被析构两次,第二次是越界访问则会报错!

正常的是不调用基类的析构函数,可以看到依次顺序是基类构造-派生类构造-派生类析构-基类析构

image-20230307162611706

继承和友元

友元不能继承,即基类不能访问派生类的私有成员和保护成员

父类的友元可以访问父类的成员,也可以访问子类的公有成员

image-20230307212557130

但是父类的友元不能访问子类的私有和保护成员

image-20230307212026750

如果硬要访问子类的私有或保护成员需要在子类里也写上友元声明

image-20230307212916553

继承和静态成员

基类定义了static静态成员,整个类的体系(包括基类和派生类)里都只存放这样的一个成员。

image-20230307220212661

多继承,菱形继承和菱形虚拟继承

单继承:一个子类只有一个直接父类

image-20230307221818114

多继承:一个子类有两个或两个以上的直接父类

image-20230308183742403

多继承的一种情况:菱形继承

菱形继承存在的问题:在空间上会有数据冗余;在访问方式上会存在二义性

image-20230308184245995

很形象的说明:Person类中有一份string name,student类和teacher类都继承了Person类,那么各自都有一份string name。Mr.Li继承了student类和teacher类,Mr.Li类中就含有两份string name 拉

image-20230308185347732

image-20230308185916022

这里有一个A类,里面有一份_a,B类(里面有一份 _b)和C类(里面有一份 _c)都继承了A类,D类(里面有一份 _d )即继承了B类也继承了C类。

进行对象创建和对四个值赋值后,通过监视窗口可以看到,_a数据各有一份且是独立的。

image-20230308201136174

那么解决办法有其一:显示指定访问哪一个父类的成员可以解决二义性,而数据冗余无法解决。解决办法其二就是菱形虚拟继承

image-20230308190638876

虚拟菱形继承

菱形继承的成员变量只有一份且是公共的,即且以最后赋值的为准。菱形继承的子类是通过指针(虚基表指针)进入虚基表找到里面存放的偏移量,从而找到公共的成员变量

以下图为例,这里有一个A类,里面有一份_a,B类(里面有一份 _b)和C类(里面有一份 _c)都虚拟继承了A类,D类(里面有一份 _d )即继承了B类也继承了C类。

B类中有一个地址(0x00107bdc)其次是B类的成员变量,指针找到该地址即虚基表,在虚基表找到存放的偏移量(20个字节),然后在B类的这个虚基表的地址(0x004FF7C4)通过偏移量找到公共的成员变量的地址(0x004FF7C4+20=0x004FF7D8) _a(A类的成员变量)

C类也同理

image-20230308210209710

菱形继承的空间是线性排列的。当A类的空间很大时,虚拟菱形继承通过指针寻找公共成员变量的方式就节省了空间。而A类的空间很小时,虚拟继承就要给虚基表开辟一部分空间,这部分空间就比原来的菱形继承开辟的空间大了

继承与组合

class X
{
	int _x;
};

class Y :public X//继承
{
	int _y;
};

class M
{
	int _m;
};

class N//组合
{
	M _mm;//包含一个M类的对象
	int _n;
};

继承是子类能访问父类的公有和保护成员,耦合度高,即白盒复用。父类只要更改了公有或者保护成员 子类就会受到影响。可以理解为is-a的关系

而组合是指一个类只能访问另一个类的公有成员。耦合度较低。即黑盒复用。被包含的类改变了保护成员另一个类不会因此受到影响。可以理解为has-a的关系
然而大多数工程都要求高内聚,低耦合。但使用继承还是组合要根据具体环境选择。

对继承的介绍就到这里

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

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

相关文章

【算法】六大排序 插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序

本章的所有代码可以访问这里 排序 一 一、排序的概念及其运用1.1排序的概念1.2 常见的排序算法二、常见排序算法的实现1、直接插入排序2、希尔排序3、选择排序4、堆排序5、冒泡排序6、快速排序6.1霍尔法6.2挖坑法6.3前后指针法7、快速排序非递归一、排序的概念及其运用 1.1排序…

Mysql中的 IFNULL 函数的详解

目录 一、概念 二、语法 三、Demo 举例说明 创建表 加入数据 运行结果 3.1举例一 3.2举例二 3.3举例三 3.4举例四 注意事项 一、概念 在mysql中IFNULL() 函数用于判断第一个表达式是否为 NULL&#xff0c;如果第一个值不为NULL就执行第一个值。第一个值为 NULL 则返…

华为OD机试题,用 Java 解【求最大数字】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…

【模板进阶】

目录 1. 非类型模板参数 2. 模板的特化 2.1 概念 2.2 函数模板特化 2.3 类模板特化 2.3.1 全特化 3 模板分离编译 3.1 什么是分离编译 3.2 模板的分离编译 4. 模板总结 有需要的老哥可以先看看模板的介绍&#xff1a;http://t.csdn.cn/2TkUYhttp://t.csdn.cn/2TkUY 1. …

聊点不一样的|Be a Serendipper:Woman VS Man

很喜欢这样一句话&#xff1a;Be a serendipper&#xff0c;and find your own serendipity!可以理解为&#xff1a;做一个善于发现美好事物的人&#xff0c;找到属于你自己的那些美好。每个人的生活中都有 Serendipity&#xff0c;有时能被我们一眼看到&#xff0c;有时又会藏…

七段码 杨辉三角

题目&#xff1a; 小蓝要用七段码数码管来表示一种特殊的文字。 上图给出了七段码数码管的一个图示&#xff0c;数码管中一共有 77 段可以发光的二 极管&#xff0c;分别标记为 a,b,c,d,e,f,g。 小蓝要选择一部分二极管&#xff08;至少要有一个&#xff09;发光来表达字符。在…

AI已到,普通人的机会在哪里?

“普通人赚到钱很难 但是被骗到钱很容易”。每当火起来一个行业&#xff08;或者仅是一个概念&#xff09;&#xff0c;都会有人来问&#xff1a;现在去做点什么&#xff0c;能够踩上风口&#xff1f;普通人的赚钱机会在哪&#xff1f;怎么做能够暴富&#xff1f;让我们先来看看…

【卷积神经网络】中间层网络的参数归一化方法 | BN / LN / IN / GN

文章目录一、为什么神经网络需要归一化二、常用的归一化方法三、Batch Normalization四、Layer Normalization五、Instance Normalization六、Group Normalization本文主要介绍神经网络中常用的归一化方法&#xff0c;主要是在神经网络内部对中间层的输入进行归一化&#xff0c…

【论文阅读】Robust Multi-Instance Learning with Stable Instances

1、摘要与引言 以往的MIL算法遵循i.i.d假设&#xff1a;训练样本与测试样本都分别来自于同一分布中&#xff0c;而这一假设往往与现实应用中有所出入。研究人员通过计算训练样本与测试样本之间的密度比对训练样本进行加权&#xff0c;以解决分布变化带来的问题。 分布的变化发…

SpringBoot + Druid + Mybatis-Plus + Mysql 实现数据库监控

1. 简介 在日常的WEB开发中都会使用数据库存储信息。大多数情况我们只是使用了数据库&#xff0c;而无法感知业务对数据库的压力&#xff0c;从而无法有目的的提升性能。在使用数据库时&#xff0c;都会选用常见的C3P0、DBCP、Hikari、Druid连接池&#xff0c;虽然SpringBoot官…

JavaScript中的数据类型以及存储上的差别?

前言 在JavaScript中&#xff0c;我们可以分成两种类型&#xff1a; 基本类型复杂类型 两种类型的区别是&#xff1a;存储位置不同 一、基本类型 基本类型主要为以下6种&#xff1a; NumberStringBooleanUndefinednullsymbol Number 数值最常见的整数类型格式则为十进制…

最强分布式锁工具:Redisson

1 Redisson概述1.1 什么是Redisson&#xff1f;Redisson是一个在Redis的基础上实现的Java驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;。它不仅提供了一系列的分布式的Java常用对象&#xff0c;还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, Sorted…

学python的第六天---字符串

一、只出现一次的字符其他&#xff1a;round(XXX,1)忽略大小写比较字符串大小字符串几个可以使用的函数二、去掉多余的空格写法一&#xff1a;写法二&#xff1a;三、信息加密写法一&#xff1a;写法二:写法三&#xff1a;自己的写法四、单词替换五、倒排单词写法一&#xff1a…

面试官:什么是双亲委派模型?如何打破它?

本文已经收录进 JavaGuide(「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。) 参加过校招面试的同学,应该对这个问题不陌生。一般提问 JVM 知识点的时候,就会顺带问你双亲委派模型(别扭的翻译。。。)。 就算是不准备面试,学习双亲委派模型对于我…

if-else if与switch的练习1:输入两个数,输出两个数的加减乘除的值

1.if-else if的练习 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice…

所有科研人警惕,掠夺型期刊和劫持型期刊的区别,千万别投错了

当今&#xff0c;新形式的学术出版物——例如数字式或开源式的学术期刊日益普及&#xff0c;热门期刊的数量逐年增长【1】。 人们获取学术出版物也越来越容易&#xff0c;使得更多的科研人员&#xff08;特别是在低收入国家&#xff09;能够及时了解各自研究领域的最新发展态势…

ubuntu20.04搭建detectron2环境

Ubuntu22.04安装Cuda11.3 Linux下驱动安装 # 以下命令按顺序执行 sudo apt update && sudo apt upgrade -y # or sudo apt update # 查看显卡信息 ubuntu-drivers devices sudo ubuntu-drivers autoinstall # or sudo apt install nvidia-driver-510 reboot nvidia-s…

毕业设计 基于51单片机WIFI智能家居系统设计

基于51单片机WIFI智能家居系统设计1、毕业设计选题原则说明&#xff08;重点&#xff09;2、项目资料2.1 系统框架2.2 系统功能3、部分电路设计3.1 STC89C52单片机最小系统电路设计3.2 ESP8266 WIFI电路设计3.3 DHT11温湿度传感器电路设计4、部分代码展示4.1 LCD12864显示字符串…

JavaEE简单示例——Spring的入门程序

简单介绍&#xff1a; 在之前我们简单的介绍了有关于Spring的基础知识&#xff0c;那么现在我们就来一步步的把理论融入到实践中&#xff0c;开始使用这个框架&#xff0c;使用过程也是非常的简单&#xff0c;大致可以分为几个基础的步骤&#xff1a; 1.首先引入Spring的Mave…

TypeScript深度剖析:TypeScript 中泛型的理解?应用场景?

一、是什么 泛型程序设计&#xff08;generic programming&#xff09;是程序设计语言的一种风格或范式 泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型&#xff0c;在实例化时作为参数指明这些类型 在typescript中&#xff0c;定义函数&#xff0c;…