【C++】学习多态原理

目录

    • 一、虚函数表
    • 二、多态原理
    • 三、关于动态绑定与静态绑定

一、虚函数表

先来看一段代码:sizeof(Base)是多少?

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

int main()
{
	cout << sizeof(Base) << endl;
	return 0;
}

如果用以前的知识,想到的结果应该是4
但是:
在这里插入图片描述
打开调试窗口发现,除了b成员变量外,还有一个_vfptr放在对象的前面,(也有可能在后面,取决于编译器),对象中的这个指针叫做虚函数表指针(简称虚表指针)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。

再增加派生类继承Base类,改造Base类,看是否有同样的效果:

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};
int main()
{
	Base b;
	Derive d;

	return 0;
}

打开调试窗口:
1️⃣基类的
在这里插入图片描述

2️⃣派生类的
在这里插入图片描述
通过调试,可总结以下几点:

  • 派生类对象中也有一个虚表指针,是继承基类的,除了基类以外就是自己的
  • Func1和Func2是虚函数,所以放进虚表中;Func3不是虚函数,所以没有在虚表中
  • 在派生类对象中发现,Func1函数的地址被覆盖了,原因是派生类对Func1函数进行了重写。重写是语法的叫法,覆盖是原理层的叫法
  • 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr
  • 派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后

两个问题:
虚函数存在哪里?
虚表存在哪里?
///
虚函数和普通函数一样的,都是存在常量区的。只是虚函数指针存到了虚表中。
虚表也是存在常量区的。验证:(vs下)

int main()
{
	Base b;
	Derive d;

	int i = 0;
	static int j = 1;
	int* p1 = new int;
	const char* p2 = "aaaa";
	printf("栈:%p\n", &i);
	printf("静态区:%p\n", &j);
	printf("堆:%p\n", p1);
	printf("常量区:%p\n", p2);

	Base* p3 = &b;
	Derive* p4 = &d;
	printf("Base虚表地址:%p\n", *(int*)p3);
	printf("Base虚表地址:%p\n", *(int*)p4);

	return 0;
}

在这里插入图片描述
根据打印结果可知,虚表是存在常量区的

二、多态原理

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}

private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

void Func(Base* p)
{
	p->Func1();
}

int main()
{
	Base b;
	Func(&b);
	Derive d;
	Func(&d);
	
	return 0;
}

在这里插入图片描述
多态调用的条件:

  • 必须通过基类的指针或者引用调用虚函数
  • 被调用的函数必须是虚函数,且派生类要对基类的虚函数进行重写

多态调用过程:
p指向的是b对象时,p->Func在b对象的虚表中找到对应的虚函数,完成调用。p指向的是d对象时,p->Func在d对象的虚表中找到对应的虚函数,完成调用。

多态调用与普通调用的区别:

  • 多态调用运行时在所指向对象的虚表中找到虚函数的地址,与对象有关
  • 普通调用编译时确定函数地址,与类型有关

多态调用:
在这里插入图片描述
普通调用:
在这里插入图片描述

为什么要通过基类对象的指针或者引用进行调用?
在这过程中,其实是切片行为。基类的指针或者引用得到派生类中继承基类的那一部分,然后再去对应的虚表中找到要调用的虚函数。

在这里插入图片描述

为什么基类对象调用不行,会变成普通调用?
用基类对象去调用,派生类对象给基类对象是切片,但注意:这过程中有拷贝构造出一个基类的对象,这个基类的对象所对应的虚表是基类的虚表,结果导致拷贝构造的基类对象都去基类对象的虚表中找虚函数,就没有调用派生类对象的虚表中的虚函数了。
在这里插入图片描述

注意:同一个类的对象,对应的是同一张虚表

为什么派生类要对基类的虚函数进行重写?
因为派生类继承基类得到基类的那部分的虚表,虚表里面的虚函数地址是基类的,如果不进行重写,在派生类对象的虚表中找到的虚函数依然是基类的,所以要对基类的虚函数进行重写,虚表中也同时完成了虚函数地址的覆盖,调用时就是派生类的了。

三、关于动态绑定与静态绑定

  • 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
  • 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

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

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

相关文章

【Linux】make 工具和 Makefile 文件的引入

前面提到了 gcc 编译器&#xff0c;那么使用 gcc 编译器肯定就会接触到 Makefile 。当源码文件比较多的时候就不适合通过直接输入 gcc 命令来编译&#xff0c;这时候就需要一个自动化的编译工具 make 。 举例&#xff1a;通过键盘输入两个整形数字&#xff0c;然后计算他们的和…

elasticSearch原理浅尝

终于等到你 马上就要放弃 开个玩笑 &#xff0c;进入正题 on fire 基础的咱不说了&#xff0c;一搜一麻袋 读 全文检索&#xff1a; 协调节点广播查询请求到相关分片 并 将其响应 整合 全局排序 返回结果集合 带路由&#xff1a;具体文档 shard hash(document_id) % (…

国外服务器托管需要了解哪些信息

国外服务器托管服务提供了一种在国外租用并管理服务器的方式&#xff0c;适用于需要特定地域服务或对本地法规有特殊要求的企业和个人。那么想要进行国外服务器托管需要了解哪些信息呢?Rak部落小编为您整理发布国外服务器托管相关内容。 以下是一些关于国外服务器托管服务的详…

YoloV8改进策略:BackBone改进|ELA

文章目录 摘要1、引言2、相关工作3、方法3.1、重新审视坐标注意力3.1.1、坐标注意力3.1.2、坐标注意力的不足 3.2、高效局部注意力3.3、多个ELA版本设置3.4、可视化3.5、实现 4、实验4.1、实验细节4.2、ImageNet上的图像分类4.3、目标检测4.4、语义分割 5、结论 摘要 https://…

如何搭建自己的百度网盘目录树搜索系统

许多做虚拟资源的小伙伴都有好几个百度网盘账号&#xff0c;大部分也都是扩容盘&#xff0c;但是扩容盘不能搜索&#xff0c;这个就很难受&#xff0c;不能让用户搜索自己的资源&#xff0c;这无意是是对成交概率是致命的&#xff0c;几百万条的数据&#xff0c;不能让用户一个…

【Java】jdk1.8 Java代理模式,Jdk动态代理讲解(非常详细,附带class文件)

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 一、什么是代理模式 想要学代理模式&#xff0c;我们就要先弄清一个概念“什么是代理”&#xff1f; 在我们的现实生活中&#xff0c;你或许不少听过关于代理的名词&#xff0c;如&#xff1a;代理商。那什么又叫做代理…

Linux课程____LVM(逻辑卷管理器)

LVM 技术是在硬盘分区和文件系统之间添加了一个逻辑层&#xff0c;它提供了一个抽象的卷组&#xff0c;可以把多块硬盘进行卷组合并。 这样一来&#xff0c;用户不必关心物理硬盘设备的底层架构和布局&#xff0c;就可以实现对硬盘分区的动态调整。 动态调整磁盘容量&#xff…

设计模式——工厂模式01

工厂模式 定义&#xff1a;工厂模式是创建子类实例化对象的一种方式&#xff0c;屏蔽了创造工厂的内部细节。把创建对象与使用对象进行拆分&#xff0c;满足单一职责。如果需要向工厂中添加新商品&#xff0c; 只需要扩展子类再重写其工厂方法&#xff0c;满足开闭原则。 设计…

数据分析(三)线性回归模型实现

1. 惩罚线性回归模型概述 线性回归在实际应用时需要对普通最小二乘法进行一些修改。普通最小二乘法只在训练数据上最小化错误&#xff0c;难以顾及所有数据。 惩罚线性回归方法是一族用于克服最小二乘法&#xff08; OLS&#xff09;过拟合问题的方法。岭回归是惩罚线性回归的…

新型智慧城市大数据解决方案(附下载)

随着云计算、大数据、移动互联网等技术的发展&#xff0c;由城市运行产生的交通、环境、市政、商业等各领域数据量巨大&#xff0c;这些数据经过合理的分析挖掘可产生大量传统数据不能反映的城市运行信息&#xff0c;已成为智慧城市的重要资产。 在大数据时代&#xff0c;数据信…

Unity入门

Unity入门学习 知识概述&#xff1a; Unity环境搭建 1.Unity引擎是什么 2.软件下载安装 下载最新的长期支持版即可 3.新工程和工程文件夹 Unity界面基础 1.Scene场景和Hierachy层级窗口 练习&#xff1a; 2.Game游戏和Project工程 3.Inspector检查和Console控制台 练习&#…

【快速上手ESP32(基于ESP-IDFVSCode)】03-定时器

ESP32中的通用定时器 通用定时器是 ESP32 定时器组外设的驱动程序。ESP32 硬件定时器分辨率高&#xff0c;具有灵活的报警功能。定时器内部计数器达到特定目标数值的行为被称为定时器报警。定时器报警时将调用用户注册的不同定时器回调函数。 在ESP32-S3中&#xff0c;一共有…

HTML:框架

案例&#xff1a; <frameset cols"5%,*" ><frame src"left_frame.html"><frame src"right_frame.html"> </frameset> 一、<frameset>标签 <frameset>标签&#xff1a;称为框架标记&#xff0c;将一个HTML…

全网最强JavaWeb笔记 | 万字长文爆肝JavaWeb开发——day06_数据库-MySQL-02

万字长文爆肝黑马程序员2023最新版JavaWeb教程。这套教程打破常规&#xff0c;不再局限于过时的老套JavaWeb技术&#xff0c;而是与时俱进&#xff0c;运用的都是企业中流行的前沿技术。笔者认真跟着这个教程&#xff0c;再一次认真学习一遍JavaWeb教程&#xff0c;温故而知新&…

vue-cli打包 nodejs内存溢出 vue2.x Last few GCs

遇到这种情况百度各种博客&#xff0c;什么改package.json里的配置&#xff0c;什么安装increase-memory-limit &#xff0c;都尝试了并没什么用处&#xff0c;最后解决方案为执行下方名单&#xff0c;再次打包就成功了&#xff1a; export NODE_OPTIONS--max_old_space_size4…

spring事务那些事

实际工作中还会面临千奇百怪的问题&#xff0c;看下面返个例子&#xff08;注意MySql数据库测试&#xff09;&#xff1a; //1.hello1Service 调用 hello2Service Transactional(propagation Propagation.REQUIRED,rollbackFor Exception.class) public void doUpdate() {//…

重建大师地物实体shp该怎样获取?(如下图)

问题如图 一般是基于自己的模型去提取的&#xff0c;可以使用地物外轮廓功能生成&#xff0c;或者这边有DLG也可以实现。同时&#xff0c;用重建大师可以提取地物外轮廓。 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件&#xff0c;输入倾斜照片&#x…

微信小程序 ---- 慕尚花坊 代码优化

代码优化 1. 分享功能 思路分析&#xff1a; 目前小程序页面都没有配置分享功能&#xff0c;需要给小程序页面设置分享功能。 但是并不是所有页面都需要设置分享功能&#xff0c; 具体哪些页面需要设置分享功能&#xff0c;可以和产品经理进行协商。 首页商品列表商品详情…

[StartingPoint][Tier1]Crocodile

Task 1 What Nmap scanning switch employs the use of default scripts during a scan? (哪些 Nmap 扫描开关在扫描期间使用默认脚本&#xff1f;) -sC Task 2 What service version is found to be running on port 21? 发现端口 21 上运行的服务版本是什么&#xff1f…

前端 基于响应式数据 实现拖拽排序和移动

在外层父元素添加拖拽相关监听事件 <divdragstart"handleDragstart"dragover"handleDragover"dragenter"handleDragenter"drag"handleDrag"drop"handleDrop">其中&#xff0c;start是drag起始元素&#xff0c;over会…