多态(C++)

多态(C++)

本文如果有错误或者不足的地方,希望各位大佬多多指点。

【本文目录】

  • 1.多态的概念
  • 2.多态的定义及实现
  • 3.抽象类
  • 4.多态的原理
  • 5.单继承和多继承的虚函数表

在这里插入图片描述


1.多态的概念

多态的概念就是:多种形态

多态就是可以有多种的形态。不同的身份去实现同一件事情,会有不同状态。例如当你是学生时,去买火车票会有学生的专属优惠,但是当你是社会人士时,就不会有这样的优惠。就像电影变形金刚中的汽车可以用多种形态。
在这里插入图片描述

2.多态的定义及实现

2.1.虚函数

被关键字virtual所修饰的函数,称为虚函数

2.2.多态的构成条件🚩

  • 子类重写父类的虚函数
  • 父类的指针或引用来调用

2.3.重写

函数名、返回值、参数列表一致(协变除外)

注意:在子类中,不添加关键字virtual也构成重写,因为从基类继承下来的虚函数,也具有虚函数的属性。

2.4.多态的实现


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

class Student :public Person
{
public://子类重写父类的虚函数
	virtual void BuyTickets() { cout << "票价--学生价" << endl; }
};

//父类的引用 ,父类的指针也可以
void fun(Person& p)
{
	p.BuyTickets();
}

int main()
{
	Person p;
	Student s;
	fun(p); // 不同的对象调用会有不同的状态
	fun(s);
	return 0;
}

3.抽象类

类中包含纯虚函数的类称为抽象类

3.1纯虚函数

纯虚函数就是在虚函数后面加上=0,内部有纯虚函数的类是无法实例化对象的。子类也会继承这个特性。子类只有重写这个纯虚函数才可以实例化对象。

class A
{
public:
	virtual void show() = 0; //纯虚函数
};

class B : public A
{
public:
	virtual void show() {} //子类重写父类的纯虚函数
};

int main()
{
	//A a; // err 无法实例化
	B b; //没有重写纯虚函数时,无法实例化对象
	return 0;
}

4.多态的原理

4.1虚函数表

下面这个类的是多少?

class Person
{
public:
	virtual void show() {};
protected:
	int _p;
};

int main()
{
    Person p;
	cout<< sizeof(p) << endl; //这个对象多大?
	return 0;
}

答案:这个对象的大小在32位平台是8字节,在64位平台下是16字节。

看图

在这里插入图片描述

解析:其中__vfptr是一个指针,叫做虚函数表指针,一个含有虚函数的类至少存在一个虚函数表指针,该指针指向的是虚函数表,虚函数表简称虚表。虚表可以看做一个数组,内部存放的虚函数的地址,一般这个数组最后存放的是nullptr。

指针在32位平台下占4字节,在64位平台占8字节空间。因此在32位平台对象p的大小是8字节空间,而在64位平台大小是16字节,在64位下时需要内存对齐因此是16字节,不是12字节。

虚函数存在哪?虚表存在哪?

  • 代码段

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

class Student :public Person
{
public://子类重写父类的虚函数
	virtual void BuyTickets() { cout << "票价--学生价" << endl; }
};

void Print()
{
	int i = 0;//存放栈区
	int* pt = &i;
	int* pi = new int; //存放堆区
	const char* pc = "hello";
	printf("%p\n", pt);
	printf("%p\n", pi);
	printf("%p\n", pc);
	printf("%p\n",&(Person::BuyTickets));

}

int main()
{
	Print();
	return 0;
}

输出结果:
在这里插入图片描述

根据实例分析:分别打印栈区、堆区、代码段的变量地址得出,虚函数表和虚函数时存放在代码段的。

class Person
{
public:
	virtual void show1() {};
	virtual void show() {};
protected:
	int _p;
};
class Student:public Person
{

public:
	virtual void show() {};
	virtual void show2() {};
protected:
	int _ps;

};

int main()
{
	Person p;
	Student s;
	return 0;
}

打开VS2022的监视窗口

在这里插入图片描述

看图解析:

  1. 基类和派生类都有一个虚函数指针(__vfptr)

  2. 派生类是由两部份组成,一部分继承基类的成员,一部分是自己的成员。

3.派生类从基类继承下来的show1函数,并没有重写时,会发现和基类的show1函数地址是一样的。派生类中的show函数重写父类的show函数,会发现地址不同的。可以知道当子类重写父类的虚函数时,会进行覆盖的父类的虚函数。

【总结】

  • 编译器会将基类的虚表拷贝一份给派生类,当派生类重写基类的虚函数时,会把基类虚函数覆盖

4.2多态的原理

进行上面的分析,可以知道子类的虚函数重写会覆盖父类的虚函数。下面进行分析多态的原理

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

class Student :public Person
{
public://子类重写父类的虚函数
	virtual void BuyTickets() { cout << "票价--学生价" << endl; }
};

void fun(Person& p)
{
	p.BuyTickets();
}

int main()
{
	Person p;
	fun(p);
	Student s;
	fun(s);
	return 0;
}

在这里插入图片描述

解析:

  • 当fun函数的调用参数是Person类的对象时,p的指向就是Person类的虚函数
  • 当fun的函数调用参数是Student类的对象时,子类重写父类的虚函数,将其覆盖,此时p的指向是Student的虚函数

4.3动态绑定和静态绑定

【静态绑定】

静态绑定也称为前期绑定(早绑定),在编译期间确定程序的行为。例如函数重载

【动态绑定】

动态绑定也称为后期绑定(晚绑定),在程序运行期间,根据拿到的类型确定程序的行为,调用具体的函数。也叫做动态多态

5.单继承和多继承的虚函数表

【单继承】

class C1 
{
public:
	virtual void fun1() {}
	virtual void fun2() {}
};

class C2:public C1
{
public:
	virtual void fun1() {}
	virtual void fun3() {}
	virtual void fun4() {}
};

typedef void(*VFPTR) ();
void PrintVPTable(VFPTR p[])
{
	cout << "虚函数指针" << p << endl;
	for (int i = 0; p[i] != nullptr; i++)
	{
		printf("第 %d 个函数地址:OX%x\n",i,p[i]);
		VFPTR f = p[i];
		f();
	}
	cout << endl;
}

int main()
{
	C1 c1;
	VFPTR* VFPTR_c1 = (VFPTR*)(*(int*)&c1); 
	PrintVPTable(VFPTR_c1);
	C2 c2;
	VFPTR* VFPTR_c2 = (VFPTR*)(*(int*)&c2);
	PrintVPTable(VFPTR_c2);

	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当子类继承父类的成员时,子类内部具有父类同时也具有子类成员,当子类调用父类成员时,会进行切割行为。子类重写虚函数时,切割子类中的父类成员,覆盖父类的拷贝出来虚函数表。

【多继承】

class C1 
{
public:
	virtual void fun1() { cout << "C1::fun1" << endl; }
	virtual void fun2() { cout << "C1::fun2" << endl; }
};

class C2
{
public:
	virtual void fun1() { cout << "C2::fun1" << endl; }
	virtual void fun2() { cout << "C2::fun2" << endl; }
};

class C3:public C2,public C1
{
public:
	virtual void fun1() { cout << "C3::fun1" << endl; }
	virtual void fun3() { cout << "C3::fun3" << endl; }
	virtual void fun4() { cout << "C3::fun4" << endl; }
};

typedef void(*VFPTR) ();
void PrintVPTable(VFPTR p[])
{
	cout << "虚函数指针" << p << endl;
	for (int i = 0; p[i] != nullptr; i++)
	{
		printf("第 %d 个函数地址:OX%x -> ",i,p[i]);
		VFPTR f = p[i];
		f();
	}
	cout << endl;
}

int main()
{
	C1 c1;
	VFPTR* VFPTR_c1 = (VFPTR*)(*(int*)&c1); 
	PrintVPTable(VFPTR_c1);
	C2 c2;
	VFPTR* VFPTR_c2 = (VFPTR*)(*(int*)&c2);
	PrintVPTable(VFPTR_c2);
	C3 c3;
	VFPTR* VFPTR_c3 = (VFPTR*)(*(int*)&c3);
	PrintVPTable(VFPTR_c3);
	return 0;
}

在这里插入图片描述

看图得知:

  • 多继承的派生类未重写的虚函数会放第一个继承虚函数表中
  • 多继承的派生类重写虚函数时,会进行全部覆盖

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

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

相关文章

【JavaScript】P2 JavaScript 书写位置

本博文总结&#xff1a; JavaScript 书写位置&#xff1a; 内部外部行内 注意事项&#xff1a; 书写的位置尽量写到 </body> 之前外部 js 标签中间不写任何内容&#xff0c;因为不予以展示 正文&#xff1a; 交互效果示例 一个简单的交互效果示例&#xff1b; <…

Echarts图表库推荐以及使用Echarts实现饼图端头弧形效果

推荐Echarts图表库官方链接http://www.ppchart.com/#/ 下面是一段实现饼图端头弧形效果的Echarts代码 虽然有了上面的图表库&#xff0c;里面案例也挺多&#xff0c;但是就是没找到我想要的这种效果&#xff0c;索性就手写了一个 下面代码可以直接去我上面的图标库运行看效果…

【CTF Web】CTFShow web11 Writeup(RCE+PHP+代码审计)

web11 1 阿呆听完自己菜死了&#xff0c;自己呆了。决定修好漏洞&#xff0c;绝对不能让自己再菜死了。 解法 可知 flag 在 config.php。 <?php # flag in config.php include("config.php"); if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/system…

线性稳压电路和开关稳压电路

稳压二极管稳压电路 电网电压增大&#xff0c;导到u1端的电压增大&#xff0c;从而使输出电压&#xff0c;稳压二极管两端的电压增大&#xff0c;稳压二极管两端电压增大&#xff0c;使流过的电注增大。那么&#xff0c;流过线性电阻R的总电流增大。 Ur电压增大&#xff0c;从…

数据挖掘与机器学习——分类算法

目录 机器学习算法最普通分类&#xff1a; 分类算法的定义&#xff1a; 分类算法的应用&#xff1a; 分类器实现分类&#xff1a; 分类器的构建标准&#xff1a; 概率模型&#xff1a; 贝叶斯公式&#xff1a; 朴素贝叶斯算法&#xff08;朴素贝叶斯分类器&#xff09;…

ShardingSphere使用案例

文章目录 一、分表1. 项目架构搭建2. 数据库搭建3. 案例开发一、分库1. 创建新的库2. 修改配置文件一、分表 1. 项目架构搭建 创建Maven项目导入相关依赖<dependencies><

【头歌】计算机网络DHCP服务器配置第二关access口配置答案

头歌计算机网络DHCP服务器配置第二关access口配置操作步骤 任务描述 本关任务&#xff1a;创建 vlan &#xff0c;并且将与 pc 机相连接口划分 vlan 。 操作要求 在第一关的拓扑图的基础上&#xff0c;配置交换机&#xff0c;具体要求如下&#xff1a; 1、在特权模式下进入 vla…

WebGL技术在教育培训中的应用

WebGL技术在教育培训中的应用非常广泛&#xff0c;通过其强大的三维图形处理能力&#xff0c;能够为教育培训提供更加生动、互动和沉浸式的学习体验。以下是WebGL在教育培训中的几个主要应用及其具体实现。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xf…

萨科微和金航标

宋仕强说&#xff0c;在现代企业中&#xff0c;员工每天都会花费大量的时间在办公工具上。这些办公工具可以协助员工快速完成工作任务&#xff0c;更潜移默化地规范和影响员工的行为&#xff0c;引导他们的思考方向&#xff0c;促进公司经营目标的达成。对于萨科微kinghelm&…

C# 反射GetProperties和GetFields的坑

有时候使用反射&#xff0c;获取类的所有字段和所有属性&#xff0c;一般情况下是按照我们写的先后顺序返回的。 但是我今天碰到了一次不是按照顺序返回的&#xff01;&#xff01;&#xff01; 翻看文档&#xff1a; GetProperties&#xff1a; https://learn.microsoft.com/…

【Sql Server】随机查询一条表记录,并重重温回顾下自定义函数的封装和使用

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言随机查询语…

56. UE5 RPG 给敌人添加AI实现跟随玩家

在这一篇里&#xff0c;我们要实现一下敌人的AI&#xff0c;敌人也需要一系列的行为&#xff0c;比如朝向英雄攻击&#xff0c;移动&#xff0c;在满足条件时施放技能。这些敌人的行为可以通过使用UE的内置的AI系统去实现。 在UE里&#xff0c;只要是基于Character类创建的蓝图…

螺旋矩阵(算法题)

文章目录 螺旋矩阵解题思路 螺旋矩阵 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]解题思路 模…

YOLOv8+PyQt5动物检测系统完整资源集合(yolov8模型,从图像、视频和摄像头三种路径识别检测,包含登陆页面、注册页面和检测页面)

1.猫狗猴检测YOLOv8&#xff08;https://mbd.pub/o/bread/mbd-ZpaTl51u&#xff09;_哔哩哔哩_bilibili 资源包含可视化的动物检测系统&#xff0c;基于最新的YOLOv8训练的动物检测模型&#xff0c;和基于PyQt5制作的可视化动物检测系统&#xff0c;包含登陆页面、注册页面和检…

Java---Cloneable接口---浅克隆和深克隆

在Java中&#xff0c;我们如何实现一个对象的克隆呢&#xff1f; 在Java中实现对象的克隆&#xff0c;我们要用到Cloneable接口。克隆也分为浅克隆和深克隆。 1.实现浅克隆 1.重写clone方法 当我们想直接通过前面已经建立好的对象来调用Object类中的clone方法时&#xff0c;…

摸鱼大数据——Hive表操作——分区表

1、介绍 特点: 分区表会在HDFS上产生目录。查询数据的时候使用分区字段筛选数据&#xff0c;可以避免全表扫描&#xff0c;从而提升查询效率 注意: 如果是分区表&#xff0c;在查询数据的时候&#xff0c;如果没有使用分区字段&#xff0c;它回去进行全表扫描&#xff0c;会降低…

positivessl泛域名证书500元13个月

随着创建网站的门槛变低&#xff0c;不论是个人用户还是企事业单位用户创建的域名网站也越来越多&#xff0c;怎么维护网络环境的安全成为了各个用户需要解决的问题。为了保护网站的数据安全&#xff0c;防止恶意攻击和数据泄露&#xff0c;大多数用户选择为域名网站安装数字证…

Laravel 图片添加水印

和这个配合使用 Laravel ThinkPhP 海报生成_laravel 制作海报-CSDN博客 代码 //水印 $x_length $imageInfo[0]; $y_length $imageInfo[1];$color imagecolorallocatealpha($posterImage, 255, 255, 255, 70); // 增加透明度参数alpha$font_size 40; //字体大小 $angle …

YOLOv10涨点改进:如何魔改注意力进行二次创新,高效替换PSA | NEU-DET为案列进行展开

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进&#xff1a;替换YOLOv10中的PSA进行二次创新&#xff0c;1&#xff09;EMA替换 PSA中的多头自注意力模块MHSA注意力&#xff1b;2&#xff09; EMA直接替换 PSA&#xff1b; 在NEU-DET案列进行可行性验证&#xff0c;1&#x…

WebGIS 智慧城市三维可视化综合管控

智慧城市可视化建设不仅提升了城市管理的科技含量和效率&#xff0c;还促进了城市可持续发展&#xff0c;提升了居民的生活质量。随着技术的不断发展和应用&#xff0c;智慧城市可视化建设将会更加丰富和完善&#xff0c;为城市发展带来更加广阔的前景。 图扑应用自研 HT for W…