第六层:继承

news2024/11/17 2:30:59

文章目录

  • 前情回顾
  • 继承
    • 继承的作用
    • 继承的基本语法
      • 继承方式
        • 公共继承
        • 保护继承
        • 私有继承
    • 继承中的对象模型
    • 继承中的构造和析构顺序
    • 继承中同名成员访问
      • 非静态成员
      • 静态成员
    • 多继承
      • 语法
      • 注意
      • 多继承中的对象模型
      • 多继承父类成员名相同
    • 菱形继承
      • 概念
      • 菱形继承出现的问题
        • 虚继承
  • 步入第七层
  • 本章知识点(图片形式)

🎉welcome🎉
✒️博主介绍:一名大一的智能制造专业学生,在学习C/C++的路上会越走越远,后面不定期更新有关C/C++语法,数据结构,算法,Linux,ue5使用,制作游戏的心得,和大家一起共同成长。
✈️C++专栏:C++爬塔日记
😘博客制作不易,👍点赞+⭐收藏+➕关注

前情回顾

在第五层中,我遇到了在C++中第二个重载,操作符重载,它与函数重载差别很大,对于操作符重载,本质其实为函数,只是将函数名换成了C++提供给程序员特定的名字,同时第五层的力量也掌握到了,要踏入第六层了…

继承

当脚接触到第六层的地面的时候,那到声音随之飘来:“这第六层可不简单,它的力量是面向对象的第二大特性——继承,希望你不要让我失望…”

继承的作用

继承是面向对象的三大特性之一,在C++中,有些类的关系如下图:
在这里插入图片描述
可以发现,这些类,下级的成员除了会拥有上级的特性,还拥有自己的特性,这个时候如果一个一个封装,会造成很多重复的代码,这里就可以用继承的力量,减少大量的重复代码。

继承的基本语法

在浏览一些网站的时候,比如:csdn、牛客,都会发现,它们都有公共的头部和底部,那我自己的csdn主页来说:
在这里插入图片描述
在这里插入图片描述
不一样的是中间的内容不相同,那如果用普通方法打印一下这个网页的基本内容会是什么样的?

#include<iostream>
using namespace std;

class A1
{
public:
	void head()
	{
		cout << "博客主页头部" << endl;
	}
	void middle()
	{
		cout << "A1的博客内容" << endl;
	}
	void  bottom()
	{
		cout << "博客主页底部" << endl;
	}
};
class A2
{
public:
	void head()
	{
		cout << "博客主页头部" << endl;
	}
	void middle()
	{
		cout << "A2的博客内容" << endl;
	}
	void  bottom()
	{
		cout << "博客主页底部" << endl;
	}
};
class A3
{
public:
	void head()
	{
		cout << "博客主页头部" << endl;
	}
	void middle()
	{
		cout << "A2的博客内容" << endl;
	}
	void  bottom()
	{
		cout << "博客主页底部" << endl;
	}
};
void test1()
{
	A1 a1;
	A2 a2;
	A3 a3;
	a1.head(); a1.middle(); a1.bottom();
	cout << "=======================" << endl;
	a2.head(); a2.middle(); a2.bottom();
	cout << "=======================" << endl;
	a3.head(); a3.middle(); a3.bottom();
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
每个都封装成类,看到是可以进行正常的输出的,但是,可以发现,重复代码很多,这些是不必要的,那现在就可以试试继承的写法,继承的基本语法为:

  • class 子类 : 继承方式 父类
    {
    子类的特点
    }
  • 子类也可以被叫做派生类
  • 父类也可以被叫做基类

普通用法中,可以将重复的代码,封装到父类当中,将每个子类特别的成员封装在子类里面,那继承方式是什么?

继承方式

继承方式一共有三种:

  1. 公共继承(public)
  2. 保护继承(protected)
  3. 私有继承(private)

在这里插入图片描述

公共继承

  • 父类所有权限不变继承到子类当中,但是不能访问私有权限的成员

保护继承

  • 父类除了私有权限的其他权限都变成保护权限继承到子类当中,私有权限内成员依然不能访问

私有继承

  • 父类中所有权限变成私有权限继承到子类当中,私有权限内成员依然访问不到

那了解了继承方式后,怎么用继承的方式写出上面的网页呢:

#include<iostream>
using namespace std;

class A
{
public:
	void head()
	{
		cout << "博客主页头部" << endl;
	}
	void  bottom()
	{
		cout << "博客主页底部" << endl;
	}
};
class A1:public A
{
public:
	void middle()
	{
		cout << "A1的博客内容" << endl;
	}
};
class A2 :public A
{
public:
	void middle()
	{
		cout << "A2的博客内容" << endl;
	}
};
class A3: public A
{
public:
	void middle()
	{
		cout << "A2的博客内容" << endl;
	}
};
void test1()
{
	A1 a1;
	A2 a2;
	A3 a3;
	a1.head(); a1.middle(); a1.bottom();
	cout << "=======================" << endl;
	a2.head(); a2.middle(); a2.bottom();
	cout << "=======================" << endl;
	a3.head(); a3.middle(); a3.bottom();
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

继承中的对象模型

从父类继承过来的成员,那些属于子类?在继承方式当中提到,私有权限是访问不到的?那是否继承到了子类当中?

#include<iostream>
using namespace std;

class A
{
public:
	int _a;
protected:
	int _b;
private:
	int _c;
};
class A1 :public A
{
public:
	int _d;
};
void test1()
{
	A1 a1;
	cout << sizeof(a1) << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
可以看到,父类内三个int占12,子类一个int占4,加起来正好是16,而输出的结果也是16,说明,父类内所有非静态成员会被继承到子类当中,只是父类内私有权限的成员被编译器藏起来了,不能进行访问,但是确实继承到了子类当中。

继承中的构造和析构顺序

那对于构造和析构呢?是先有的父类还是子类?可以试着用代码进行验证:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "父类构造调用" << endl;
	}
	~A()
	{
		cout << "父类析构调用" << endl;
	}
};
class A1:public A
{
public:
	A1()
	{
		cout << "子类构造调用" << endl;
	}
	~A1()
	{
		cout << "子类析构调用" << endl;
	}
};
void test1()
{
	A1 a1;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
可以看到,构造为先有父后由子,析构则反过来。

继承中同名成员访问

非静态成员

当子类和父类中出现了非静态同名成员,如何通过子类对象,访问子类或者父类中的非静态同名成员呢?

  • 子类:直接访问
  • 父类:需要加作用域

这里用代码进行验证:

#include<iostream>
using namespace std;

class A
{
public:
	void a()
	{
		cout << "父类中调用" << endl;
	}
};
class A1:public A
{
public:
	void a()
	{
		cout << "子类中调用" << endl;
	}
};
void test1()
{
	A1 a1;
	a1.a();
	a1.A::a();
}
int main()
{
	test1();
	return 0;
}


成员属性的调用也与函数调用一样,可以用代码来进行验证:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{
		_a = 10;
	}
	void a()
	{
		cout << "父类中调用" << endl;
	}
	void a(int a)
	{
		cout << "父类第二个函数调用" << endl;
	}
	int _a;
};
class A1:public A
{
public:
	A1()
	{
		_a = 20;
	}
	void a()
	{
		cout << "子类中调用" << endl;
	}
	int _a;
};
void test1()
{
	A1 a1;
	cout << a1._a << endl;
	cout << a1.A::_a << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

那如果在父类中,发生函数重载,子类能直接调用参数不同的那个函数吗?

、#include<iostream>
using namespace std;

class A
{
public:
	void a()
	{
		cout << "父类中调用" << endl;
	}
	void a(int a)
	{
		cout << "父类第二个函数调用" << endl;
	}
};
class A1:public A
{
public:
	void a()
	{
		cout << "子类中调用" << endl;
	}
};
void test1()
{
	A1 a1;
	a1.a(10);
	a1.A::a();
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
编译器直接报错,程序是跑不起来的,这里是因为,在子类中出现与父类同名的成员函数,子类的同名函数会直接隐藏掉父类中所有的同名成员函数。

静态成员

静态成员的访问有两种:

  1. 通过对象访问
  2. 通过类名访问

对于这两个方法,第一种与非静态成员是一样的,可以验证一下第二种——通过类名访问:

#include<iostream>
using namespace std;

class A
{
public:
	static void a()
	{
		cout << "父类中调用" << endl;
	}
};
class A1:public A
{
public:
	static void a()
	{
		cout << "子类中调用" << endl;
	}
};
void test1()
{
	A1 a1;
	A1::a();
	A1::A::a();
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
静态函数发生函数重载时也与非静态函数发生函数重载一样。

多继承

  • 在C++种,允许一个类继承多个类,这个时候就是多继承

语法

  • class 子类: 继承方式 父类1 , 继承方式 父类2 …

注意

  • 在多继承中,多个父类中可能有同名成员出现,这个时候需要加作用域来区分

多继承中的对象模型

那当子类继承多个父类的时候,每个父类内的非静态成员都会被继承过去吗?通过代码验证一下:

#include<iostream>
using namespace std;

class A
{
public:
	int a;
	int b;
};
class A1
{
public:
	int _a;
	int _b;
};
class AA :public A, public A1
{
public:
	int _a1;
	int _b1;
};
void test1()
{
	AA aa;
	cout << sizeof(aa) << endl;
	
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
和普通继承一样,每个父类的非静态成员都会被继承到子类当中。

多继承父类成员名相同

  • 假设现在两个父类,有两个成员名时一样的,那子类能直接访问这个成员吗?
#include<iostream>
using namespace std;

class A
{
public:
	int _a;
	int b;
};
class A1
{
public:
	int _a;
	int _b;
};
class AA :public A, public A1
{
public:
	int _a1;
	int _b1;
};
void test1()
{
	AA aa;
	aa._a;
	
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
编译器是直接报错的,是因为编译器不知道这个成员是来自与哪个父类,这个情况在C++中有个专业名词,叫做二义性,这个时候要怎么样才能访问到呢?就需要进行加作用域,假设现在访问A内的_a:

#include<iostream>
using namespace std;

class A
{
public:
	int _a;
	int b;
};
class A1
{
public:
	int _a;
	int _b;
};
class AA :public A, public A1
{
public:
	int _a1;
	int _b1;
};
void test1()
{
	AA aa;
	aa.A::_a = 10;
	cout<<aa.A::_a<<endl;
	
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

菱形继承

概念

  • 两个子类继承了同一个父类,又有一个类,同时继承了两个子类

在这里插入图片描述

菱形继承出现的问题

  • 马和驴都继承了动物的数据,在骡子使用数据的时候,就会产生二义性,骡子继承动物的数据只需要一份,那应该怎么办?

虚继承

对于上面所说的二义性的问题,因为同样的数据只需要一份,这个时候就可以在继承的时候加上关键字:

  • vritual

将继承变成虚继承,这个时候最大的父类就为虚基类,可以用代码试验:

#include<iostream>
using namespace std;

class A
{
public:
	int a;
	int b;
};
class AA:virtual public A
{


};
class AA1:virtual public A
{

};
class AAA :public AA, public AA1
{

};
void test1()
{
	AAA a;
	a.AA::a = 10;
	a.AA1::a = 20;
	cout << a.AA::a << endl;
	cout << a.AA1::a << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
发现,两个数据修改输出的结果是一样的,这个时候就是虚继承起到的作用,让将内部数据共享了一个,这个时候最小的子类也可以直接访问到这个属性:

#include<iostream>
using namespace std;

class A
{
public:
	int a;
	int b;
};
class AA:virtual public A
{


};
class AA1:virtual public A
{

};
class AAA :public AA, public AA1
{

};
void test1()
{
	AAA a;
	a.AA::a = 10;
	a.AA1::a = 20;
	cout << a.a << endl;
}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述
这是为什么呢?那这个时候从父类继承下来些什么?这个时候从父类继承下来一个指针:

  • vbptr

这个指针叫做虚基类指针,它会指向一个叫做:

  • vbtable

虚基类表,虚基类表中有一个偏移量,这个偏移量会指向一个数据,每个子类指向的数据都是同一份,这个时候数据就只会有一个。
在这里插入图片描述

步入第七层

石碑倒下,大片尘土飞扬,尘土散去,只留下一串脚印,通往第七层…

本章知识点(图片形式)

在这里插入图片描述

😘预知后事如何,关注新专栏,和我一起征服C++这座巨塔
🚀专栏:C++爬塔日记
🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉

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

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

相关文章

【数据分析】(task3)数据重构

note 数据的合并&#xff1a;df自带的join方法是横向合并&#xff0c;append方法是纵向&#xff08;上下&#xff09;合并拼接&#xff1b;pd的merge方法是横向合并&#xff0c;然后用刚才的apend进行纵向合并。数据的重构&#xff1a;stack函数的主要作用是将原来的列转成最内…

Redis基本类型和基本操作

2.Redis常见命令 Redis是典型的key-value数据库&#xff0c;key一般是字符串&#xff0c;而value包含很多不同的数据类型&#xff1a; Redis为了方便我们学习&#xff0c;将操作不同数据类型的命令也做了分组&#xff0c;在官网&#xff08; https://redis.io/commands &…

阿里云轻量服务器下>安装宝塔面板>安装使用Tomcat服务器>通过公网ip地址>直接访问网站目录下文件

第一步 阿里云开放Tomcat 8080端口号 和宝塔面板 8888端口 第二步 如果你的应用镜像 一开始在阿里云购买服务器时候没有选择宝塔应用镜像 先打开如下界面 将系统中应用镜像 确定更换为 宝塔面板镜像 第三步 请在应用详情中 走完紫色所框选的步骤 第四步 将上一步获取到的…

cadence SPB17.4 - allegro - align component by pin

文章目录cadence SPB17.4 - allegro - align component by pin概述笔记实验备注补充 - 2023_0120_2337ENDcadence SPB17.4 - allegro - align component by pin 概述 allegro自带的元件对齐, 默认是对齐元件中心的, 对齐后的效果是中心对齐. 但是为了走线能走的最短, 拉线方便…

2023年春节祝福第二弹——送你一只守护兔(下),CSS3 动画相关属性图例实例大全(82种),守护兔源代码免费下载

2023年春节祝福第二弹——送你一只守护兔(下&#xff09; CSS3 动画相关属性图例实例大全&#xff08;82种&#xff09;、守护兔源代码免费下载 本文目录&#xff1a; 五、CSS3 动画相关属性实例大全 &#xff08;1&#xff09;、CSS3的动画基本属性 &#xff08;2&#xf…

TCP/IP OSI七层模型

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.OSI七层模型 1.什么是OSI七层参考模型 2.七层每层分别的作用…

Spring热部署设置

手动热部署 热部署是指在不停止应用程序的情况下更新应用程序的功能。这样可以避免短暂的服务中断&#xff0c;并且可以更快地部署新的功能和修复问题。热部署通常适用于Web应用程序和服务器端应用程序。 在pom.xml中添加依赖&#xff1a; <dependency><groupId>…

cmake 02 hello_cmake

cmake 学习笔记 一个最小化的 cmake 项目 目录结构 F:\2023\code\cmake\hello_cmake>tree /f 卷 dox 的文件夹 PATH 列表 卷序列号为 34D2-6BE8 F:. │ CMakeLists.txt │ main.cpp │ └─.vscodelaunch.jsontasks.jsonF:\2023\code\cmake\hello_cmake>源码 main.c…

Java 集合 笔记

体系 Collection接口 List接口&#xff1a;按照顺序插入数据&#xff0c;可重复 ArrayList实现类&#xff1a;LinkedList实现类&#xff1a; Set接口&#xff1a;不可重复的集合 HashSet实现类 Queue接口&#xff1a;队列 LinkedList实现类ArrayBlockingQueue实现类PriorityQu…

Python CalmAn工具包安装及环境配置过程【Windows】

文章目录CalmAn简介安装要求我的设备1>CalmAn压缩包解压2>conda创建虚拟环境3>requirements依赖包配置&#xff08;包括tensorflow&#xff09;4>caiman安装(mamba install)5>caimanmanager.py install6>PyCharm添加解释器7>Demo演示8>遇到的问题CalmA…

DB SQL 转 ES DSL(支持多种数据库常用查询、统计、平均值、最大值、最小值、求和语法)...

1. 简介 日常开发中需要查询Elasticsearch中的数据时&#xff0c;一般会采用RestHighLevelClient高级客户端封装的API。项目中一般采用一种或多种关系型数据库(如&#xff1a;Mysql、PostgreSQL、Oracle等) NoSQL(如&#xff1a;Elasticsearch)存储方案&#xff1b;不同关系数…

【SAP Abap】X档案:SAP ABAP 中 AMDP 简介及实现方法

SAP ABAP 中 AMDP 简介及实现方法0、前言1、AMDP 简介1.1 代码下沉&#xff08;Code Pushdown&#xff09;1.2 AMDP 是托管数据库过程的容器1.3 AMDP 的优缺点1.4 几种数据库访问方式的区别1.5 几种数据库访问方式的选用1.6 使用的开发工具2、实现方法2.1 AMDP PROCEDURE&#…

Linux自带10种常用性能分析与监控工具

liunx的性能分析与监控这些问题是一个很重要的问题&#xff0c;我们需要解决这个问题就可以借助liunx中的一些工具来帮我们处理掉这个问题&#xff0c;以下将会讲一下目前liunx中常用自带的性能分析与监控工具 Linux自带10种常用性能分析与监控工具1.vmstat2.iostat3.iotop监控…

uniapp cli的使用

uniapp官方文档有很多地方写的不是很明白。写笔记还是非常有必要的。 cli入门 uniapp的cli分为两种&#xff1a;uni cli和hbuilder cli。下面是官方对于两者的定义。官方实际上是更推荐uni cli的。因为官方文档通篇都是介绍uni cli&#xff0c;也是优先介绍uni cli的。hbuild…

Linux系统之Bonding 网卡绑定配置方法

Linux系统之Bonding 网卡绑定配置方法一、检查本地系统环境1.检查系统版本2.查看服务器网卡二、创建网卡配置文件1.进入网卡配置文件目录2.拷贝eth0的网卡配置文件3.修改bond0网卡配置文件4.修改eth1网卡配置文件5.修改eth2网卡配置文件三、创建bonding的配置文件1.编辑bonding…

OneFlow v0.9.0正式发布

今天是 OneFlow 开源的第 903 天&#xff0c;OneFlow v0.9.0 正式发布。本次更新包含 640 个 commit&#xff0c;完整更新列表请查看链接&#xff1a;https://github.com/Oneflow-Inc/oneflow/releases/tag/v0.9.0&#xff0c;欢迎下载体验新版本&#xff0c;期待你的反馈。One…

Java补充内容(Junit 反射 注解)

1 Junit测试 测试分类&#xff1a; 1. 黑盒测试&#xff1a;不需要写代码&#xff0c;给输入值&#xff0c;看程序是否能够输出期望的值。 2. 白盒测试&#xff1a;需要写代码的。关注程序具体的执行流程。 Junit使用&#xff1a;白盒测试 步骤&#xff1a; 定义一个测试类(测试…

机器学习知识总结——18.实现一个简单的K-Means聚类

文章目录引用库生成样本数据训练K-Means实验在上一章节里简要的介绍了无监督学习中聚类的知识点&#xff0c;看的太多理论概念&#xff0c;难免会有点莫名其妙&#xff0c;现在就让我们来实现一个简单的 K-Means 算法&#xff0c;从而从原理上彻底弄明白聚类是怎么工作的。 引…

YOLO v2主要看这些重点

来源&#xff1a;投稿 作者&#xff1a;ΔU 编辑&#xff1a;学姐 往期内容&#xff1a; YOLOv1学习笔记 论文 《YOLO9000:Better, Faster, Stronger》 Joseph Redmon∗†, Ali Farhadi∗† University of Washington∗ , Allen Institute for AI† http://pjreddie.com/…

计算机视觉OpenCv学习系列:第五部分、颜色操作

第五部分、颜色操作第一节、颜色表操作1.查找表LUT(look up table)2.颜色查找表&#xff08;1&#xff09;Gamma校正&#xff08;2&#xff09;OpenCV默认的查找表3.代码练习与测试&#xff08;1&#xff09;多种颜色查找表&#xff08;2&#xff09;滚动条颜色查找表第二节、随…