C++之多态(中篇)(最全总结)

news2025/4/4 9:30:04

这里接上面C++之多态(上篇)

本篇目录

  • 4.多态的原理
    • 4.2 多态的原理
    • 4.3 C++ 11 override和final
    • 4.4 重载、重写(覆盖)、隐藏(重定义)的对比 (函数之间的关系)
  • 5.抽象类
    • 5.1概念
    • 5.2接口继承和实现继承
  • 6.单继承和多继承关系的虚函数表

4.多态的原理

4.2 多态的原理

从上面虚函数的分析中我们已经知道了多态的原理,接下来我们从更深层次去探索多态。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
	virtual void Func1(){}
};

class Student :public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-半价" << endl;
	}
	virtual void Func2(){}
};

int main()
{
	//同一个类型的对象共用一个虚表
	Person p1;
	Person p2;

	//vs下,不管是否完成重写,子类虚表跟父类虚表都不是同一个
	Student s1;
	Student s2;
	return 0;
}

在这里插入图片描述
通过上图我们发现,同一个类型的对象共用同一个虚表,vs下,不管是否完成重写,子类虚表跟父类虚表都不是同一个,除此之外,我们发现,vs的监视窗口下,子类自己的虚函数Func2(), 它是在子类自己的虚函数表中的,但是vs的监视窗口却没有显示出来,下面我们用一段程序将其展示出来。
虚表的本质是一个函数指针数组
在这里插入图片描述
在这里插入图片描述

通过上图我们发现对象虚表的地址在它对象的地址处的值取前4个字节即可。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "Person::买票-全价" << endl;
	}
	virtual void Func1()
	{
		cout << "Person::Func1()" << endl;
	}
};

class Student :public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "Student::买票-半价" << endl;
	}
	virtual void Func2()
	{
		cout << "Student::Func2()" << endl;
	}
};

typedef void(*VFPTR)();

//void PrintVFTable(VFPTR table[])//打印虚函数表
void PrintVFTable(VFPTR* table,size_t n)//打印虚函数表中的虚函数地址并且调用虚函数
{
	//for (size_t i = 0; table[i] != nullptr; ++i)
	for (size_t i = 0; i < n; ++i)
	{
		printf("vft[%d]:%p->", i, table[i]);
		//table[i]();
		VFPTR pf = table[i];//有点类似与强制类型转换
		pf();
	}
	cout << endl;
}

int main()
{
	//同一个类型的对象共用一个虚表
	Person p1;
	Person p2;

	//vs下,不管是否完成重写,子类虚表跟父类虚表都不是同一个
	Student s1;
	Student s2;

	//取对象头部虚函数表指针传递过去
	//这里的2,3是我们知道对象的虚函数的个数
	PrintVFTable((VFPTR*)*(int*)&p1,2);
	PrintVFTable((VFPTR*)*(int*)&s1,3);
	return 0;
}

在这里插入图片描述

4.3 C++ 11 override和final

从上面可以看出,C++对函数的重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名的字母次序写反而无法构成重载,而这种错误在编译期间是不会报出来的,只有程序运行时没有得到预期结果才来debug会得不偿失,因此:C++ 11提供了override和final这两个关键字,可以用来帮助用户检测是否重写。

1.final:修饰虚函数,表示该虚函数不能被重写

class Car
{
public:
	virtual void Drive()final{}
};

class Benz :public Car
{
public:
	virtual void Drive() //error
	{
		cout << "Benz-舒适" << endl;
	}
};

2.override:检查派生类虚函数是否重写基类某个虚函数,如果没有编译报错

class Car
{
public:
	virtual void Drive(){}
};

class Benz :public Car
{
public:
//检查子类虚函数是否完成重写
	virtual void Drive()override
	{
		cout << "Benz-舒适" << endl;
	}
};

4.4 重载、重写(覆盖)、隐藏(重定义)的对比 (函数之间的关系)

在这里插入图片描述

5.抽象类

5.1概念

在虚函数的后面写上 =0,则这个函数为纯虚函数,包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

class Car
{
public:
	virtual void Drive() = 0;
};

class Benz :public Car
{
public:
	virtual void Drive()
	{
		cout << "Benz-舒适" << endl;
	}
};

class BwM:public Car
{
public:
	virtual void Drive()
	{
		cout << "BMW-操控" << endl;
	}
};

int main()
{
	//Car c;//抽象类不能实例化对象
	//BwM b;

	Car* ptr = new BwM;
	ptr->Drive();//多态的体现

	ptr = new Benz;
	ptr->Drive();
	return 0;
}

在这里插入图片描述

5.2接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。

6.单继承和多继承关系的虚函数表

需要注意的是在单继承和多继承关系中, 下面我们去关注的是派生类对象的虚表模型,因为基类的虚表模型前面我们已经看过了,没什么需要特别研究的。

class Base1
{
public:
	virtual void func1()
	{
		cout << "Base1::func1" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2" << endl;
	}
private:
	int b1 = 1;
};

class Base2
{
public:
	virtual void func1()
	{
		cout << "Base2::func1" << endl;
	}
	virtual void func2()
	{
		cout << "Base2::func2" << endl;
	}
private:
	int b2 = 2;
};


class Derive :public Base1, public Base2
{
public:
	virtual void func1()
	{
		cout << "Derive::func1" << endl;
	}

	virtual void func3()
	{
		cout << "Derive::func3" << endl;
	}
private:
	int d = 3;
};

typedef void(*VFPTR)();

//void PrintVFTable(VFPTR table[])//打印虚函数表
void PrintVFTable(VFPTR* table,size_t n)//打印虚函数表中的虚函数地址并且调用虚函数
{
	//for (size_t i = 0; table[i] != nullptr; ++i)
	for (size_t i = 0; i < n; ++i)
	{
		printf("vft[%d]:%p->", i, table[i]);
		table[i]();
		VFPTR pf = table[i];//有点类似与强制类型转换
		pf();
	}
	cout << endl;
}

int main()
{
	Derive d;

	PrintVFTable((VFPTR*)*(int*)&d,3);//打印Base1虚函数表
	PrintVFTable((VFPTR*)*(int*)((char*)&d + sizeof(Base1)), 2);//法一:打印Base2虚函数表
	//法二:打印Base2虚函数表
	Base2* ptr2 = &d;//切片得到Base2的地址
	PrintVFTable((VFPTR*)(*(int*)ptr2), 2);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
更深层次的问题
观察上图我们发现Base1中的func1和Base2中的func1都被Derive进行了重写,它们的内容是一样的,应该指向同一份函数,但是它们的地址为什么不一样呢?

下篇文章我们会揭晓!!!

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

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

相关文章

三、基于kubeadm安装kubernetes1.25集群第二篇

在上一篇中我们已经安装kubernetes要求做了服务器初始化&#xff0c;看这篇之前&#xff0c;建议先看下上篇&#xff1a;https://blog.csdn.net/u011837804/article/details/128350651 那我们正式开始kubernetes1.26集群安装 1、每台机器安装docker20.10.22 docker的安装细节…

数据结构训练营4

开启蓝桥杯备战计划&#xff0c;每日练习算法一题&#xff01;&#xff01;坚持下去&#xff0c;想必下一年的蓝桥杯将会有你&#xff01;&#xff01;笔者是在力扣上面进行的刷题&#xff01;&#xff01;由于是第一次刷题&#xff01;找到的题目也不咋样&#xff01;所以&…

itop-imx8m开发板gstreamer日志级别设置

gst 的日志等级分为 none(0)error(1) warning(2) info(3) debug(4) log(5)。默认 gst 的日志等级为 1&#xff0c;即 error 打印&#xff0c;出错时会打印。 1&#xff09;全局日志级别设置 如果需要更高级别打印&#xff0c;修改环境变量 GST_DEBUG 即可。如需要 warning 级别…

基于python的transform行人车辆识别

Transformer是一种神经网络体系结构&#xff0c;由于它能够有效地处理顺序数据中的长期依赖性&#xff0c;因此在自然语言处理(NLP)任务中受到欢迎。它还被应用于各种其他任务&#xff0c;包括图像分类、对象检测和语音识别。 在车辆和行人识别方面&#xff0c;transformer可用…

浅谈安科瑞电能预付费系统在大电力客户中的设计及应用分析

摘 要 随着我国供电企业的不断发展&#xff0c;而用电模式也在不断改革&#xff0c;预付费技术在气、电等部门得到普遍的使用&#xff0c;本文主要针对预付费系统在大电力客户中的使用情况进行分析&#xff0c;提高用电用户的缴费率&#xff0c;有效的避免了客户恶意偷窃电行…

【小5聊】Winform从指定服务器下载文件的方式

在一些实际项目中&#xff0c;我们往往需要上传一些excel、word等办公文件&#xff0c;甚至是mp3、mp4等音频视频文件。 当然&#xff0c;大多数小型网站会放到自己服务器&#xff0c;如果文件量不大的话 如果文件数量会很多&#xff0c;那么就需要考虑使用第三方来保管存储 不…

Elasticsearch:使用 NLP 问答模型与你喜欢的圣诞歌曲交谈

自然语言处理 (NLP) 是人工智能 (AI) 的一个分支&#xff0c;旨在通过将计算语言学与统计、机器学习和深度学习模型相结合&#xff0c;尽可能接近人类解释地理解人类语言。 NLP 的最大挑战之一是在考虑到各种语言表示的情况下预训练文本数据的过程。 2018 年&#xff0c;谷歌…

企业报表插件怎么用?

最近因为单位年底要做部门总结汇报&#xff0c;需要使用很多的数据以报表的形式来引证今年的工作情况&#xff0c;内部的 OA、ERP 这些业务系统是指望不上了。作为懂一点 SQL、又经常用 Excel 来给领导做报表的我&#xff0c;在网上一轮操作&#xff08;就是百度啦&#xff09;…

算法通关手册 刷题笔记1 数组基础

算法通关手册 刷题笔记1 数组基础 持续更新中 文章目录算法通关手册 刷题笔记1 数组基础数组操作题目0189 轮转数组AC自己的解法其他解法知识点查漏补缺关于python中的数组赋值python中对象的引用0066 加一AC自己的解法其他解法知识点查漏补缺0724 寻找数组的中心下标AC自己的解…

大数据必学Java基础(一百一十六):Application域监听器

文章目录 Application域监听器 一、认识Application域监听器 二、监听器代码 Application域监听器 一、认识Application域监听器

关于金字塔格式转tiff并且openslide能获取到缩略图等标签信息

我们都知道openslide通过openslide_get_associated_image_names获取相关的图像名称&#xff0c;比如"thumbnail",“label”,“macro"等。那我们将"thumbnail”,“label”,"macro"写入tiff的时候&#xff0c;如何才能保证openslide能够获取到呢&a…

2022年第三届MathorCup高校数学建模挑战赛——大数据竞赛 赛道B 北京移动用户体验影响因素研究 建模方案及代码实现(更新中)

【BetterBench原创】2022年第三届MathorCup高校数学建模挑战赛——大数据竞赛 赛道B 北京移动用户体验影响因素研究 建模方案及代码实现&#xff08;更新中&#xff09; 更新进展 2022年12月20日 22:00 发布初步思路 &#xff08;待更新&#xff09;发布初步思路实现代码 …

即时通讯音视频开发视频编解码预测技术

目的是去除空间冗余和时间冗余。因为视频存在大量的空间冗余和时间冗余&#xff0c;包括空间冗余、时间冗余&#xff0c;预测后得到去除大部分空间或时间冗余的残差。 图像空间相邻像素具有很强的相关性&#xff0c;帧内预测技术去除空间冗余。 视频图像在时间上有较强的相关性…

组团出海抢抓跨境电商外贸,有利于2023“开门稳”

组团出海抢抓跨境电商外贸&#xff0c;有利于2023“开门稳” 我国是货物贸易第一大国&#xff0c;强大的生产能力、出色的产业配套、完善的物流供应、充足高效的劳动力&#xff0c;都为我国成为贸易强国提供了有利条件。 近几年&#xff0c;中国跨境电商产业进入快速发展阶段&a…

区块链北大肖老师学习笔记4

第五节 比特币系统的实现 区块链是去中心化的账本&#xff0c;比特币使用的是基于交易的这种账本模式(transaction[交易]-based ledger[账本])。系统当中并不会显示每个账户有多少钱。 比特币系统的全节点要维护一个叫UTXO(unspent transaction output)(还没有被花出去的交易的…

成为2.2亿儿童主动要吃的天然营养,AMSTRONG维小壮做对了什么?

文|螳螂观察&#xff08;TanglangFin&#xff09; 随着疫情防控政策的调整&#xff0c;在人们开始注重提升身体免疫力以抗击病毒的措施中&#xff0c;在国外流行多年的接骨木莓&#xff0c;开始受到国内消费者的青睐。 接骨木莓的功效早已被美国FDA和加拿大FDA双重认证&#…

VUE3-Pinia的使用《三》

pinia的官网是简介 | Pinia。 它的功能和vuex差不多&#xff0c;但是pinia更加优于vuex。主要用于状态管理&#xff0c;管理全局的变量&#xff0c;也可以存储页面A的值&#xff0c;然后在页面B中直接访问&#xff0c;不分父子组件之间的关系&#xff0c;可以任意传值&#xf…

设计模式之状态模式

State design pattern 状态模式的概念、状态模式的结构、状态模式的优缺点、状态模式的使用场景、状态模式的实现示例、状态模式的源码分析 1、状态模式的概念 对有状态的对象&#xff0c;把复杂的判断逻辑提取到不同的状态对象中&#xff0c;允许状态对象在其内部状态发生改变…

使用预授权签名加速 BSV 有状态合约更新

状态更新竞争 BSV 智能合约将状态存储在交易链的输出中。 当交易花费包含旧状态的输出并创建包含新状态的输出时&#xff0c;就会发生状态转换。 在任何给定时间&#xff0c;交易链末端的单个输出/UTXO 都具有最新状态。 当多个事务竞争同时更新共享状态时&#xff0c;就会出…

Anchor2

Anchor也是第二季了~~ 来一起来学习Faster R-CNN中的RPN及Anchor 说到RPN和Anchor&#xff0c;应该立马就能想到Faster R-CNN网络框架 首先我先将几类经典的目标检测网络做一个对比&#xff0c;然后开始说说今天要讲的知识。 最开始出现的是R-CNN&#xff0c;如下图&#xff…