C++修炼之路之继承<二>

目录

一:子类的六大默认成员函数 

二:继承与友元

三:继承与静态成员

四:复杂的继承关系+菱形继承+菱形虚拟继承 

1.单继承

2.多继承 

3.菱形继承;一种特殊的多继承 

4.菱形虚拟继承

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

不使用虚拟继承前数据冗余的情况

使用虚拟继承数据的情况

经典例题

五:继承和组合

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧

衔接上篇的内容继续介绍

一:子类的六大默认成员函数 

1.默认的意思就是我们不写,编译器会自动生成一个,在子类中的默认成员函数要把父类单独看成一个独立的对象来完成对应操作

父类的默认函数

class Person
{
public:
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};

实现子类的对应函数操作

class Student :public Person
{
public:
	Student(const char* name,int id)
		:Person(name)//作为一个单独对象构造
		,_id(id)
	{
		cout << "Student(const char* name, int id)" << endl;
	}
	Student(Student& s)
		:Person(s)//父子类的赋值规则
		, _id(s._id)
	{
		cout << "Student(const Student& s)" << endl;
	}
	Student& operator=(Student& s)
	{
		if (this != &s)
		{
			_id = s._id;
			Person::operator = (s);//父子类的赋值规则
		}
		cout << "Student& operator=(const Student& s)" << endl;

		return *this;
	}
	~Student()
	{
		//Person::~Person();
		cout << "~Student()" << endl;
	}
protected:
	int _id;
};

 特殊的对于子类此处的析构函数,不能显示调用父类的析构,原因为

1.由于多态的原因,析构函数统一会被处理成destructor

2.父子类的析构函数构成隐藏

3.为了保证析构安全,先子后父(如果先父后子的话,在父类析构后,在子类还可以访问父类的成员,此时就会导致访问已经释放的空间,导致越界访问),构造为先父后子

4.父类析构函数不需要显示调用,子类析构函数结束时会自动调用父类析构,保证先子后父

练习:如何实现一个不能被继承的类

方法为:可以将父类的成员函数或者整个成员访问方式设置为private,这样继承的时候无论继承方式为那种,都是不可见的

在c++11中新添加了final关键字,使用的话意味着该类不能被继承

二:继承与友元

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

如要访问子类的私有和保护成员的话,在子类中也声明友元关系(就是基类的友元声明在派生类声明一下)

三:继承与静态成员

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

练习:计算创建多少个类对象--子类初始化时不管我们写不写都会走初始化列表,也一定会走父类的构造

class Person
{
public:
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};

int Person::_count = 0;

class Student : public Person
{
protected:
	int _stuNum; // 学号
};

Student func()
{
	Student st;
	return st;
}

int main()
{
	Person p;
	Student s;
	func();

	cout << Student::_count << endl;

	return 0;
}

四:复杂的继承关系+菱形继承+菱形虚拟继承 

1.单继承

一个子类只有一个直接父类

2.多继承 

一个子类有两个或以上直接父类

3.菱形继承;一种特殊的多继承 

从这里就可以看出,在Assistant的对象中就有两份Person成员,这就导致数据冗余和二义性的问题,这样在访问数据的时候就不知道具体访问哪一个

4.菱形虚拟继承

于是提出了使用虚继承即添加关键字virtual来解决,这是就只有一份Person成员,只初始化一次

如:

class Person
{
public:
	string _name; // 姓名
	int _age;
	int _tel;
	// ...
};
class Student : virtual public Person
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a.Student::_name = "小张";
	a.Teacher::_name = "老张";
	a._name = "张三";
}

int main()
{
	Test();

	return 0;
}

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

不使用虚拟继承前数据冗余的情况

发现在B子类 和C子类中都有一份父类A的数据,造成数据冗余

使用虚拟继承数据的情况

 这时父类的数据只有一份,子类访问的都是同一父类的数据

这时就会看到在 添加virtual的子类中会多存储一个地址,这个地址其实存储的是距离A的偏移量

此时A同属于B和C,这样B和C该如何找到公共的A呢,这里是通过了B和C的两个指针,指向的一张表,这两个指针叫虚基表指针,这两个表叫虚基表,虚基表中存的是偏移量,通过偏移量找到A

这个偏移量就是从该位置向后跳跃的字节数,然后找到并访问父类的成员数据 

经典例题

 

对于p1和p3指向的位置相同,但访问权限不同 

打印顺序为什么?

对于任何的派生类的构造都要 调用基类的构造

注意:初始化列表出现的顺序是按声明顺序来执行的,不是执行顺序

所以结果为 class A,class B ,class C,class D

一般不建议设计出菱形继承

五:继承和组合

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

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

对于这两个的大小都是一样大的, 

但对于继承父类中的protected 成员可以在子类中访问

对于组合的话,一个类中的protected成员,另一个类不能使用,不能直接调用另一个类的public成员函数

3.

4.实际尽量多去使用组合,组合的耦合度低,代码的可维护性好。对于有些关系就适合继承就使用继承,对于多态,也必须实现继承,对于既能用组合又能继承的,就使用组合 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

javaagent使用

Java Agent是什么&#xff1f; Java Agent是Java平台提供的一个强大工具&#xff0c;它可以在运行时修改或增强Java应用程序的行为。是在JDK1.5以后引入的&#xff0c;它能够在不影响正常编译的情况下修改字节码&#xff0c;相当于是在main方法执行之前的拦截器&#xff0c;也叫…

启明云端ESP32-S3+车载桥接器案例,能实现对车载产品集控

最近房车旅行很盛行&#xff0c;谁不想五一自驾游开车去外面玩&#xff1f;为了能提升用户体验&#xff0c;车企房车智能化升级越来越普遍&#xff0c;接下来小启给大家讲一个案例&#xff0c;启明云端ESP32-S3车载桥接器&#xff0c;感兴趣的可以看看。 一、ESP32-S3车载桥接器…

实验:使用FTP+yum实现自制yum仓库

实验准备 FTP服务器端&#xff1a;centos-1&#xff08;IP:10.9.25.33&#xff09; 客户端&#xff1a;centos-2 两台机器保证网络畅通&#xff0c;原yum仓库可用&#xff0c;已关闭防火墙和selinux FTP服务器端 ①安装vsftpd并运行&#xff0c;设定开机自启动 安装vsftpd…

免费ssl通配符证书申请教程

在互联网安全日益受到重视的今天&#xff0c;启用HTTPS已经成为网站运营的基本要求。它不仅保障用户数据传输的安全&#xff0c;提升搜索引擎排名&#xff0c;还能增强用户对网站的信任。通配符证书是一种SSL/TLS证书&#xff0c;用于同时保护一个域名及其所有下一级子域名的安…

​面试经典150题——对称二叉树

1. 题目描述 2. 题目分析与解析 2.1 思路一——递归 为了解决问题“检查一个二叉树是否是对称的”&#xff0c;我们需要判断树的左子树和右子树是否是彼此的镜像。这意味着树的左子树的左侧应该与右子树的右侧相同&#xff0c;左子树的右侧应该与右子树的左侧相同。 定义问题…

数字工厂管理系统的应用场景主要有哪些

随着信息技术的飞速发展&#xff0c;数字工厂管理系统逐渐成为了工业制造领域的一大亮点。这一系统集成了物联网、大数据、云计算、人工智能等先进技术&#xff0c;实现了对工厂生产流程的智能化、高效化管理。那么&#xff0c;数字工厂管理系统究竟在哪些应用场景中发挥着重要…

Unity 3D定点数物理引擎实战系列:BEPU物理引擎碰撞计算与碰撞规则的架构与设计

前面我们讲解了如何监听物理引擎的碰撞事件, 在物理引擎内核中如何架构与设计碰撞规则,使得物理Entity与周围的物理环境产生碰撞时&#xff0c;如何灵活的控制物理碰撞&#xff0c;本节給大家详细的讲解BEPUphysicsint 物理引擎内部是如何管理与控制碰撞规则的。本文主要讲解3个…

项目二:学会使用python爬虫请求库(小白入门级)

上一章已经了解python爬虫的基本知识&#xff0c;这一次让我们一起来学会如何使用python请求库爬取目标网站的信息。当然这次爬虫之旅相信我能给你带来不一样的体验。 目录 一、安装requests 库 简介 安装 步骤 1.requests的基本使用3步骤 2.查看所使用编码 3.设置编码…

Qt菜单栏

文章目录 创建菜单栏创建菜单并在菜单栏中添加创建子菜单并添加到菜单创建菜单项并在菜单中添加分割线实现简易的记事本 Qt 窗口是通过 QMainWindow类 来实现的 创建菜单栏 Qt 中的菜单栏是通过 QMenuBar 这个类来实现的&#xff0c;一个窗口最多只有一个菜单栏。 菜单栏包含…

动态库静态库linux

动态库静态库 静态库 静态库必须包含在可执行文件里&#xff0c;整个都要包含 缺点&#xff1a;消耗系统大&#xff0c;每个使用静态库的程序都要复制静态库&#xff08;浪费内存&#xff09; 影响使用场景&#xff1a; 在静态库内存小的时候&#xff0c;可以用来提升速度 制…

学习MQ异步

1.MQ异步调用的优势 事件驱动模式&#xff1a; 优势&#xff1a; 总结&#xff1a; 2.初识MQ 核心概念以及结构&#xff1a; 常见的消息模型&#xff1a; 基本消息队列模型&#xff1a; 生产者代码&#xff1a; Testpublic void testSendMessage() throws IOException, Timeo…

亿级电商实时数据分析平台构建实战

基于FlinkClickHouse构建亿级电商实时数据分析平台&#xff08;PC、移动、小程序&#xff09; 引用网络文章开启本课程的开篇&#xff1a; 在大数据分析领域中&#xff0c;传统的大数据分析需要不同框架和技术组合才能达到最终的效果&#xff0c;在人力成本&#xff0c;技术能…

比较转录组学方法推断基因共表达网络及其在玉米和水稻叶片转录组中的应用 TO-GCN时序分析-文献精读-8

Comparative transcriptomics method to infer gene coexpression networks and its applications to maize and rice leaf transcriptomes 比较转录组学方法推断基因共表达网络及其在玉米和水稻叶片转录组中的应用 TO-GCN时序分析&#xff0c;媲美加权基因共表达网络分析-WG…

2024.4.16

多进程并发服务器 #include<myhead.h> #define SER_IP "192.168.125.54" #define SER_PORT 8888 void handler(int signo) {if(signoSIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);} } int main(int argc, char *argv[]) {//将SIGCHLD信号与处理函数绑定if(…

数据孤岛是业务效率的无声杀手

数据孤岛是组织的一个常见问题&#xff0c;因为它们可能对数据可访问性、数据完整性和数据管理造成障碍。当组织内的不同部门或团队拥有用于存储数据的数据库或系统&#xff0c;并且没有用于所有数据的中央存储库时&#xff0c;就会出现数据孤岛。这可能会导致难以全面了解数据…

minio 报错The specified key does not exist.

minio 报错The specified key does not exist. 解决方法 检查是否把全路径包含进去了&#xff0c;桶的名称是bucketName只填写桶的名称就行了&#xff0c;objectName是文件的名称&#xff0c;只要填写文件名称就行了&#xff0c;不要填写全路径&#xff01;&#xff01;&#…

36. UE5 RPG在激活技能时使用蒙太奇动画

在上一篇文章里面&#xff0c;我们实现了一个简单的火球术&#xff0c;创建了火球术的火球&#xff0c;以及能发射它的技能。很简陋&#xff0c;在技能触发的时候&#xff0c;直接在武器的位置生成火球发射出去。在一篇文章里&#xff0c;我们要实现使用技能时&#xff0c;角色…

实验室信息系统源码 saas模式java+.Net Core版开发的云LIS系统全套源码可二次开发有演示

实验室信息系统源码 saas模式java.Net Core版开发的云LIS系统全套源码可二次开发有演示 一、技术框架 技术架构&#xff1a;Asp.NET CORE 3.1 MVC SQLserver Redis等 开发语言&#xff1a;C# 6.0、JavaScript 前端框架&#xff1a;JQuery、EasyUI、Bootstrap 后端框架&am…

梯度下降法法实现线性回归模型

一、线性回归模型 线性回归模型是一种预测性的建模技术&#xff0c;它研究的是因变量&#xff08;目标&#xff09;和自变量&#xff08;特征&#xff09;之间的关系。这种关系假设是线性的&#xff0c;意味着因变量可以通过一个或多个自变量的线性组合来预测。数学上&#xf…

视觉slam14讲-大纲-持续更新

视觉slam入门太难 数学理论编程知识计算机视觉知识 缺一不可&#xff0c;大家一起加油