波奇学C++:多态知识点

news2024/12/23 15:23:19

多态中函数的重写(基类指针访问派生类函数),只重写函数的实现,而不重写声明。

class Person
{
public:
	 virtual void fun(int i = 0)
	{
		cout << "Person"<<" "<<i;
	}
};
class Student:public Person
{
public:
	 virtual void fun(int i = 1)
	{
		cout << "student" <<" "<<i;
	}
};
int main()
{
	Person p;
	Student st;
	Person* pp = &st;
	pp->fun();

	return 0;
}

结果是 student 0 原因在于重写时只重写函数的实现,就是说相当于Person的fun的声明和Student的函数实现的拼在一起所以缺省值是0。

为什么多态调用(重写)只能用父类的指针和引用,不能子类指针或者引用,不能是父类对象?

如果是子类指针或者引用就不是多态调用了只是单存子类对父类的重定义,隐藏函数。

上一篇文章提到的,多态的本质就是基类和派生类的虚表中保存的虚函数地址被覆盖了,多态调用意味着访问的必须是子类的虚表而不是父类的。

子类对象直接赋值父类不会拷贝虚表虚函数的地址

上图为赋值前,下图为赋值后,如图他们的__vfptr始终不同,所以父类对象必然无法访问子类对象的虚函数的地址。

为什么指针和引用可以?

父类型指针表示它范围的范围是父类,所以它指向子对象时,本质上依然说访问子类的父类部分,虚表依然是子类的虚表。

引用同理,相当于切割出子类中父类的部分。本质上依然是子类的虚表。

为什么父类指针可以指向子类对象?可以指向意味着结构相似,原因在于,继承相当于把父对象一整个拷贝放在子对象中,结构相似也是向上转换的基础

虚表的存储在代码段

证明思路:输出各个区的地址和虚表的地址,进行比较,字节相差较少说明在哪个区。

int main()
{
	Person p;
	Student st;
	int a = 1;
	printf("栈上:%x\n", &a);
	int* b = new int;
	printf("堆上:%x\n", b);
	static int c = 0;
	printf("静态区:%x\n", &c);
	const char* d = "abcde";
	printf("代码段:%x\n", d);
	printf("虚表1:%x\n", *((int*)&p));
	printf("虚表2:%x\n", *((int*)&st));
	return 0;

}

注意打印对象是打印对象的成员变量的值,这里是因为__vptr内置变量(保存虚表地址)的在成员变量首位,所以可以打印出来,同时int* 是只取虚表的地址后四个字节(小端机),%x也是只打印地址的后4位字节。

通过比较可发现,虚表和代码段的位置更近,所以虚表在代码段中。

派生类新的虚函数保存在虚表中,原有虚函数的地址的下面

class Person
{
public:
	 virtual void fun(int i = 0)
	{
		cout << "Person"<<" "<<i;
	}
	 int _a;
};
class Student:public Person
{
public:
	 virtual void fun(int i = 1)
	{
		cout << "student" <<" "<<i;
	}
	 virtual void fun1()
	 {
		 cout << "new virtual fun1()";
	 }
	 int _b;
};

fun1()是Student的虚函数,fun1保存在子函数的虚表中

证明:虚表保存函数指针地址,虚表可以看成指针数组,所以我们可以把虚表的函数指针打印出来。

typedef void(*FUNC_PTR) ();//重定义函数指针类型
//形参是数组,实参为数组指针
void PrintVFT(FUNC_PTR table[])
{
    //vs会在虚表末尾保存一个空指针,所以循环到nullptr为止
	for (size_t i = 0; table[i] != nullptr; i++)
	{
		printf("[%d]:%p\n", i, table[i]);
	}
}
int main()
{
	Person ps;
	Student st;
	int vft1 = *((int*)&ps);
//86位机器地址是32位转换成int*
	PrintVFT((FUNC_PTR*)vft1);
	int vft2 = *((int*)&st);
	PrintVFT((FUNC_PTR*)vft2);
	return 0;
}

 如图上面为父类虚表保存的地址,下面为派生类虚表保存的指针地址。重写的虚函数覆盖了原有的地址,并且新地址在虚表内。

静态多态:指的是函数重载,指的是编译的时候函数地址确定了

动态多态:继承,虚函数重写,调用的函数地址的确定是在运行时去虚表中确定的

多继承的多态问题

typedef void(*FUNC_PTR) ();
void PrintVFT(FUNC_PTR table[])
{
	for (size_t i = 0; table[i] != nullptr; i++)
	{
		printf("[%d]:%p", i, table[i]);
		FUNC_PTR f = table[i];
		f();
		printf("\n");
	}
}
class Base1 {
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1;
};
class Base2 {
public:
	virtual void func1() {
		cout << "Base2::func1" << endl;
	}
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2;
};
class Derive :public Base1, public Base2
{
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1;
};
int main()
{
	Derive d;
	cout << sizeof(d) << endl;
	int vft1 = *((int*)&d);
	Base2* ptr = &d;
	int vft2 = *((int*)ptr);
	PrintVFT((FUNC_PTR*)vft1);
	PrintVFT((FUNC_PTR*)vft2);
	return 0;

}

下面代码的Derive继承了Base1和Base2,其中两个fun1()都被继承了。

打印结果

 为什么是20?

 因为是一整个对象继承,所以会存在两个虚表,base1,base2虚表指针+int变量 8+8+4=20

由上面的结果图可知fun1在两个虚表中被重写,且都调用了同一个函数。但是地址却不一样,

实际上调用虚表2的fun()的地址,会改变指针位置和虚表1fun()指针相同,再调用函数。

反汇编证明

b1,b2指针分别调用fun1(),反汇编,call指令进入func1函数,此时

注意此处fun1()的地址是0C92840h

调用base2的fun1虚表地址,此时地址是0C94670h

进入call指令,ecx-8,再jump向0C91244h地址最后到base1虚表的地址。

 

 简单来说指向base2部分的指针,先指向base1的,再调用指针1保存的重写函数的地址。

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

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

相关文章

H5页面safari浏览器底部遮挡问题解决方案亲测有效

media screen and (max-width: 767px) { _::-webkit-full-page-media, _:future, :root .safari_only { padding-bottom: 120px; //解决Safari浏览器底部遮挡问题 } } 然后给对应div加上这个类名就可以了

本地录像视频文件如何推送到视频监控平台EasyCVR进行AI视频智能分析?

安防监控平台EasyCVR支持多协议、多类型设备接入&#xff0c;可以实现多现场的前端摄像头等设备统一集中接入与视频汇聚管理&#xff0c;并能进行视频高清监控、录像、云存储与磁盘阵列存储、检索与回放、级联共享等视频功能。视频汇聚平台既具备传统安防监控、视频监控的视频能…

ESP32开发:Clion配置IDF

IDF环境搭建 使用安装包安装IDF 可以通过安装包进行安装&#xff0c;如下图&#xff1a; 下载链接如下&#xff1a;https://dl.espressif.cn/dl/esp-idf/?idf4.4 安装好后&#xff0c;IDF会添加环境变量IDF_TOOLS_PATH&#xff0c;如果要安装多个IDF&#xff0c;为了防止冲…

Java笔记:ThreadLocal

1. ThreadLocal简介 多线程访问同一个共享变量的时候容易出现并发问题&#xff0c;特别是多个线程对一个变量进行写入的时候&#xff0c;为了保证线程安全&#xff0c;一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步…

031:vue子组件向父组件传递多个参数,父组件2种解析方法

第031个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

6个超好用的视频素材网站,4K/8K高质量,免费下载。

很多视频剪辑和做自媒体的朋友都不知道去哪里找视频素材&#xff0c;而且很多网站的素材可以免费下载但是不能商用&#xff0c;还有需要付费购买使用。下面推荐几个良心网站&#xff0c;视频素材免费下载&#xff0c;还能商用&#xff0c;赶紧收藏起来吧。 1、菜鸟图库 https:…

基于Python和mysql开发的在线音乐网站系统(源码+数据库+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python和mysql开发的在线音乐网站系统(&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经…

qt 正则表达式

以上是正则表达式的格式说明 以下是自己写的正则表达式 22-25行 是一种设置正则表达式的方式&#xff0c; 29-34行 : 29行 new一个正则表达式的过滤器对象 30行 正则表达式 的过滤格式 这个格式是0-321的任意数字都可以输入 31行 将过滤格式保存到过滤器对象里面 32行 将验…

VSCode搭建Django开发环境

文章目录 一、Django二、搭建步骤1. 安装python和VSCode&#xff0c;安装插件2. VSCode打开项目文件夹3. 终端中键入命令&#xff1a;建立虚拟环境4. 选择Python的解释器路径为虚拟环境5. 在虚拟环境中安装Django6.创建Django项目7. 创建app应用8. 运行应用9. 修改配置中文显示…

JVM 虚拟机 ----> Java 类加载机制

文章目录 JVM 虚拟机 ----> Java 类加载机制一、概述二、类的生命周期1、类加载过程&#xff08;Loading&#xff09;&#xff08;1&#xff09;加载&#xff08;2&#xff09;验证&#xff08;3&#xff09;准备&#xff08;4&#xff09;解析&#xff08;5&#xff09;初始…

纯小白安卓刷机1

文章目录 常见的英文意思刷机是什么&#xff1f;为什么要刷机&#xff1f;什么是BL锁&#xff08;BootLoader锁&#xff09;&#xff1f;我的机能够刷机吗&#xff1f;什么是Boot镜像/分区&#xff1f;什么是Recovery镜像/分区&#xff08;缩写为rec&#xff09;&#xff1f;什…

2023-2024年最值得选的Java毕业设计选题大全:2000个热门选题推荐

一、前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; 毕业设计选题非常重要&a…

【数仓建设系列之五】数仓选型架构概览

【数仓建设系列之五】实时数仓选型架构概览 离线数仓&#xff08;Offline Data Warehouse&#xff09;和实时数仓&#xff08;Real-time Data Warehouse&#xff09;是数仓领域两种常见的数据存储和处理架构&#xff0c;它们在数据处理的方式、目标和时间性上有所不同&#xff…

web端三维重建算法-colmap++

vismap vismap 是colmap 版本 &#xff08;1&#xff09; 支持superpoint superglue &#xff08;2&#xff09; 支持netvlad 图像检索 &#xff08;3&#xff09;支持特征点尺度定权 &#xff08;4&#xff09;支持二维码定位 &#xff08;5&#xff09;支持融合gps &#x…

穿破行业增长迷雾,云鲸J4为何能成为“破题之钥”?

文 | 螳螂观察 作者 | 青月 这几年&#xff0c;消费者对于产品的需求一直在变。 像汽车&#xff0c;过去的消费者可能更看重车的安全性、油耗低等&#xff0c;可如今再看消费者对车的需求&#xff0c;车联网服务、自动辅助驾驶等过去被视为“边缘”的能力&#xff0c;正在变…

虚拟机Ubuntu操作系统最基本终端命令(安装包+详细解释+详细演示)

虚拟机及乌班图&#xff08;Ubuntu操作系统&#xff09; 提示&#xff1a;大家需要软件的可以直接在此链接中提取 链接&#xff1a;https://pan.baidu.com/s/1_4VHGTlXjIuVhBINeOuBCA 提取码&#xff1a;nd0c 文章目录 虚拟机及乌班图&#xff08;Ubuntu操作系统&#xff09;终…

医院不良事件管理系统源码 鱼骨图分析 跌倒事件、压疮事件、坠床事件等系统检测,智能上报

医疗不良事件报告系统源码 医疗不良事件报告系统源码旨在建立全面的、统一的医疗不良事件标准分类系统和患者安全术语&#xff0c;使不良事件上报管理更加标准化和科学化。通过借鉴国内外医疗不良事件报告系统的先进经验&#xff0c;根据医疗不良事件的事件类型、处理事件的不…

Linux高性能服务器编程 学习笔记 第二章 IP协议详解

本章从两方面探讨IP协议&#xff1a; 1.IP头部信息。IP头部出现在每个IP数据报中&#xff0c;用于指定IP通信的源端IP地址、目的端IP地址&#xff0c;指导IP分片和重组&#xff0c;指定部分通信行为。 2.IP数据报的路由和转发。IP数据报的路由和转发发生在除目标机器外的所有主…

【SSH】如何删掉远程服务器中的虚拟环境?如何删掉远程服务器中的用户?如何删掉某个文件夹?

文章目录 一、如何删掉远程服务器中的虚拟环境&#xff1f;二、如何删掉远程服务器中的用户&#xff1f;三、如何删掉某个文件夹&#xff1f; 一、如何删掉远程服务器中的虚拟环境&#xff1f; 在Linux系统下删除conda虚拟环境&#xff1a; # 删除虚拟环境 conda remove -n y…

Nginx 中 location 和 proxy_pass 的斜杠问题

location 的斜杠问题比较好理解&#xff0c;不带斜杠的是模糊匹配。例如&#xff1a; location /doc 可以匹配 /doc/index.html&#xff0c;也可以匹配 /docs/index.html。 location /doc/ 强烈建议使用这种 只能匹配 /doc/index.html&#xff0c;不能匹配 /docs/index…