当前位置: 首页 > news >正文

C++——多态底层原理

虚函数表

先来看这个问题:

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

sizeof(Base)是多少? 

答案是:8

因为Base中除了成员变量_b,还有一个虚函数表_vfptr(当类中有虚函数就会生成),虚函数表的本质是函数指针数组,用来存储虚函数的指针


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 _a = 1;
};class Derive :public Base 
{
public:void Func1(){cout << "Derive::Func1()" << endl;}
private:int _b = 2;
};void test1()
{Base bs;Derive de;
}

 

基类虚函数表

 

派生类基函数表

派生类虚函数表是这样生成的:先将基类的虚函数表内容拷贝过来,在把派生类中重写的虚函数的地址覆盖到被重写的虚函数地址上,最后将派生类自己增加的虚函数依次增加到虚表的末尾。

上面这个例子中,派生类和基类的虚函数表都存储有两个函数地址,即Func1和Func2,但是派生类只对Func1重写,没有对Func2重写,编译器会将派生类中的Func1的地址进行覆盖,所以Func1的地址不同,Func2的地址相同(0x009f1370)

注意:虚函数存在哪,虚函数表存在哪?

虚函数和普通的成员函数一样都存在公共代码段,虚函数表只是存了虚函数的指针;虚函数表存在哪,这个要看编译器,感兴趣可以自己验证。

我的编译器是将虚函数表存储在类对象的开始位置,使用32位系统虚表大小4byte

void test()
{int i = 0;printf("栈区:%p\n", &i);char* ch = new char;printf("堆区:%p\n", ch);static int ci = 1;printf("数据段:%p\n", &ci);const char* s = "hello";printf("代码段:%p\n", s);Derive de;printf("虚表:%p\n", *((int*)&de));}

虚表地址和代码段地址相差48byte,基本可以认为虚表存在代码段。大家可以参考这个方法,根据自己的编译器和环境来测试


多态原理

说了这么多,虚函数表在多态中有什么用?

当我们有基类的指针或引用调用派生类的虚函数时,第一步是将派生类赋值给基类,这时派生类只会将属于基类的那部分交给基类的指针或引用,而属于基类的那部分中包含了派生类的虚函数表。这样造成的结果就是,调用虚函数时调用了派生类中重写过的虚函数,实现了多态。

为什么直接将派生类赋值给基类就不能实现多态呢?因为这种写法会在编译器编译时,直接从符号表确定函数地址,程序运行时直接调用。而多态调用在编译期间不会确定,只在程序运行时到对象中寻找函数。其实编译器并不知道这个对象是基类还是派生类,只是无脑从虚函数表中找。


下面这个测试程序,通过观察汇编代码,就可以直观看出普通调用和多态调用的区别:

void test()
{Base bs;Derive de;bs = de;Base& b = de;bs.Func1();b.Func1();
}

多继承的虚函数表

前面都是以单继承为例讲虚函数表和多态原理,下面我们看多继承的虚函数表

class Base1
{
public:virtual void Func1(){cout << "Base1::Func1()" << endl;}virtual void Func2(){cout << "Base1::Func2()" << endl;}
private:int _a = 1;
};class Base2
{
public:virtual void Func3(){cout << "Base2::Func3()" << endl;}virtual void Func4(){cout << "Base2::Func4()" << endl;}
};
class Derive :public Base1 ,public Base2
{
public:void Func1(){cout << "Derive::Func1()" << endl;}void Func3(){cout << "Derive::Func3()" << endl;}virtual void Func5(){cout << "Derive::Func5()" << endl;}
private:int _b = 2;
};void test()
{Base1 b1;Base2  b2;Derive d;
}

调试: 

 

 

多继承后的派生类有两个虚函数表,Func2和Func4没有重写,与基类函数地址相同。Func1和Func3重写后进行覆盖。派生类新增加的虚函数地址存到了最先继承的基类Base1的虚表中(第一张图的监视窗口未显示,有bug,在内存窗口可以看到) 

重写是对虚函数函数体部分的重写

看下面程序的运行结果是什么:

 class A{public:virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}virtual void test(){ func();}};class B : public A{public:void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }};int main(int argc ,char* argv[]){B*p = new B;p->test();return 0;}

test函数没有重写,直接调用A::test();func函数被重写,虚表中是B中func的函数地址,又因为重写只是对函数体的重写,val缺省值是0,结果是B->0.

相关文章:

C++——多态底层原理

虚函数表 先来看这个问题&#xff1a; class Base { public: virtual void Func1() { cout << "Func1()" << endl; } private: int _b 1; }; sizeof(Base)是多少&#xff1f; 答案是&#xff1a;8 因为Base中除了成员变量_b,还有一个虚函数表_vfp…...

asdTools-ReID热力图可视化

文章首发见博客&#xff1a;https://mwhls.top/4869.html。 无图/格式错误/后续更新请见首发页。 更多更新请到mwhls.top查看 欢迎留言提问或批评建议&#xff0c;私信不回。 Github - 开源代码及Readme Blog - 工具介绍 摘要&#xff1a;基于TorchCam实现ReID的热力图可视化的…...

CSS学习笔记

目录 1.CSS简介1.什么是CSS2.为什么使用CSS3.CSS作用 2.基本用法1.CSS语法2.CSS应用方式1. 内部样式2.行内样式3.外部样式1.使用 link标签 链接外部样式文件2.import 指令 导入外部样式文件3.使用举例 3.选择器1.基础选择器1.标签选择器2.类选择器3.ID选择器4.使用举例 2.复杂选…...

linux操作命令

VMware版本&#xff1a; 17.0 ubantu版本&#xff1a;22.04.3 命令&#xff1a; # 查看当前目录文件 ls# 切换路径 []内是路径 cd [snap/] cd ../ #返回上一层# 创建文件 []内是文件名 touch [test.txt]# 创建文件夹 []内是文件夹名 mkdir [myself]# 测试一下网络 ping www.b…...

猜数字游戏(Python)

一、猜数字游戏是一个古老的密码破译类、益智类小游戏&#xff0c;通常由两个人参与&#xff0c;一个人设置一个数字&#xff0c;一个人猜数字&#xff0c;当猜数字的人说出一个数字&#xff0c;由出数字的人告知是否猜中&#xff1a;若猜测的数字大于设置的数字&#xff0c;出…...

可视化模块

目录 可视化送入网络的图片可视化网络层的热力图 可视化送入网络的图片 送入的数据为imgs&#xff0c;其大小为(8,3,256,256)&#xff0c;并以2行8列进行展示 import matplotlib.pyplot as plt import numpy as np# 假设你的张量名为 tensor&#xff0c;形状为 (8, 3, 256, 2…...

MyBatis insert标签

<insert id"addWebsite" parameterType"string">insert into website(name)values(#{name}) </insert> 在 WebsiteMapper 接口中定义一个 add() 方法 public int addWebsite(String name); 参数为 Sting 类型的字符串&#xff1b;返回值为 …...

扬尘监测:智能化解决方案让生活更美好

随着工业化和城市化的快速发展&#xff0c;扬尘污染问题越来越受到人们的关注。扬尘不仅影响城市环境&#xff0c;还会对人们的健康造成威胁。为了解决这一问题&#xff0c;扬尘监测成为了一个重要的手段。本文将介绍扬尘监测的现状、重要性以及智能化解决方案&#xff0c;帮助…...

【AI视野·今日NLP 自然语言处理论文速览 第四十五期】Mon, 2 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 2 Oct 2023 Totally 44 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Efficient Streaming Language Models with Attention Sinks Authors Guangxuan Xiao, Yuandong Tian, Beidi C…...

The little schemer 学习

参考文章&#xff1a; The Little Schemer 阅读笔记-CSDN博客 前言 原子是Scheme的基本元素之一。首先定义了过程atom?&#xff0c;用来判断一个S-表达式是不是原子&#xff1a; (define atom?(lambda (x)(and (not (pair? x)) (not (null? x))))) 这个“pair”实际上…...

yolov5+bytetrack算法在华为NPU上进行端到端开发

自从毕业后开始进入了华为曻腾生态圈&#xff0c;现在越来越多的公司开始走国产化路线了&#xff0c;现在国内做AI芯片的厂商比如&#xff1a;寒武纪、地平线等&#xff0c;虽然我了解的不多&#xff0c;但是相对于瑞芯微这样的AI开发板来说&#xff0c;华为曻腾的生态比瑞芯微…...

【Java-LangChain:使用 ChatGPT API 搭建系统-1】简介

简介 欢迎来到课程《使用 ChatGPT API 搭建系统》 , 旨在指导开发者如何基于 ChatGPT 搭建完整的智能问答系统。 使用 ChatGPT 不仅仅是一个单一的 Prompt 或单一的模型调用&#xff0c;本课程将分享使用 LLM 构建复杂应用的最佳实践。 本课程以构建客服助手为例&#xff0c…...

BJT晶体管

BJT晶体管也叫双极结型三极管&#xff0c;主要有PNP、NPN型两种&#xff0c;符号如下&#xff1a; 中间的是基极&#xff08;最薄&#xff0c;用于控制&#xff09;&#xff0c;带箭头的是发射极&#xff08;自由电子浓度高&#xff09;&#xff0c;剩下的就是集电极&#xff0…...

ORACLE中SQL运算符的优先级

SQL运算符优先级: 注&#xff1a; 1、可以使用括号改变优先级顺序 2、可以看出OR的优先级最低&#xff0c;算术运算符的优先级最高 另&#xff1a;操作符优先级 * / - 1、乘除的优先级高于加减&#xff1b; 2、同一优先级运算符从左向右执行&#xff1b; 3、括号内的…...

springboot和vue:十一、Axios网络请求的安装引入与使用、跨域问题解决(CORS)

Axios简介与安装 Axios是一个基于promise的网络请求库&#xff0c;作用于node.js和浏览器中Axios在浏览器端使用XMLHttpRequests发送网络请求&#xff0c;并自动完成json数据的转换安装&#xff1a;npm install axios官方文档&#xff1a;https://www.axios-http.cn/ Axios基…...

外汇天眼:真实记录,投资者在盗版MT4平台SCE Group上做交易的经历!

外汇市场是全球最大的金融市场&#xff0c;比起其他市场有着更多天然的优势&#xff0c;但也因为资讯的不对等&#xff0c;导致很多人上当受骗。而在外汇市场上最常见的骗局之一&#xff0c;就是黑平台使用盗版MT4/5交易软件&#xff0c;因为截至目前MT4/5仍是外汇市场交易使用…...

FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制

FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg 命令视频录制 FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg 命令视频录制安装软件&#xff1a;Screen Capturer Recorder查看可用设备名字音视频录制录制视频&#xff08;默认参数&#xff09;录制声音&#xff08;默认参数&am…...

html 笔记:CSS

1 什么是CSS CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素样式通常存储在样式表中 1.1 css的语法格式 1.1.1 选择器种类 HTML选择器&#xff1a; 重新定义HTML的某种标签的显示格式id选择器 对于HTML文档中的某个标签&#xff0c;定义它的显示格式…...

【LeetCode - 每日一题】901. 股票价格跨度(23.10.07)

901. 股票价格跨度 题意 设计一个数据结构返回股票当日价格的跨度&#xff08;必须是当日开始的&#xff09; 解法 暴力 优化 一开始没理解题意&#xff0c;以为是求第 i 天及以前&#xff0c;小于等于 prices[i] 的最大连续子串的长度。后来才发现&#xff0c;这个最大连…...

第二证券:突发!A股T+0?刚刚,紧急回应!

沪深生意所急迫回应 6日&#xff0c;商场传出一个消息&#xff0c;传延伸A股生意时刻和部分票可日内T0一次。一个版本是提早至9点&#xff0c;然后下午延伸至15&#xff1a;30&#xff0c;另一个版本是上午推延至12点&#xff0c;下午延伸至16&#xff1a;00。 7日&#xff0…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...