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++——多态底层原理
虚函数表 先来看这个问题: class Base { public: virtual void Func1() { cout << "Func1()" << endl; } private: int _b 1; }; sizeof(Base)是多少? 答案是:8 因为Base中除了成员变量_b,还有一个虚函数表_vfp…...
asdTools-ReID热力图可视化
文章首发见博客:https://mwhls.top/4869.html。 无图/格式错误/后续更新请见首发页。 更多更新请到mwhls.top查看 欢迎留言提问或批评建议,私信不回。 Github - 开源代码及Readme Blog - 工具介绍 摘要:基于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版本: 17.0 ubantu版本:22.04.3 命令: # 查看当前目录文件 ls# 切换路径 []内是路径 cd [snap/] cd ../ #返回上一层# 创建文件 []内是文件名 touch [test.txt]# 创建文件夹 []内是文件夹名 mkdir [myself]# 测试一下网络 ping www.b…...
猜数字游戏(Python)
一、猜数字游戏是一个古老的密码破译类、益智类小游戏,通常由两个人参与,一个人设置一个数字,一个人猜数字,当猜数字的人说出一个数字,由出数字的人告知是否猜中:若猜测的数字大于设置的数字,出…...
可视化模块
目录 可视化送入网络的图片可视化网络层的热力图 可视化送入网络的图片 送入的数据为imgs,其大小为(8,3,256,256),并以2行8列进行展示 import matplotlib.pyplot as plt import numpy as np# 假设你的张量名为 tensor,形状为 (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 类型的字符串;返回值为 …...
扬尘监测:智能化解决方案让生活更美好
随着工业化和城市化的快速发展,扬尘污染问题越来越受到人们的关注。扬尘不仅影响城市环境,还会对人们的健康造成威胁。为了解决这一问题,扬尘监测成为了一个重要的手段。本文将介绍扬尘监测的现状、重要性以及智能化解决方案,帮助…...
【AI视野·今日NLP 自然语言处理论文速览 第四十五期】Mon, 2 Oct 2023
AI视野今日CS.NLP 自然语言处理论文速览 Mon, 2 Oct 2023 Totally 44 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Efficient Streaming Language Models with Attention Sinks Authors Guangxuan Xiao, Yuandong Tian, Beidi C…...
The little schemer 学习
参考文章: The Little Schemer 阅读笔记-CSDN博客 前言 原子是Scheme的基本元素之一。首先定义了过程atom?,用来判断一个S-表达式是不是原子: (define atom?(lambda (x)(and (not (pair? x)) (not (null? x))))) 这个“pair”实际上…...
yolov5+bytetrack算法在华为NPU上进行端到端开发
自从毕业后开始进入了华为曻腾生态圈,现在越来越多的公司开始走国产化路线了,现在国内做AI芯片的厂商比如:寒武纪、地平线等,虽然我了解的不多,但是相对于瑞芯微这样的AI开发板来说,华为曻腾的生态比瑞芯微…...
【Java-LangChain:使用 ChatGPT API 搭建系统-1】简介
简介 欢迎来到课程《使用 ChatGPT API 搭建系统》 , 旨在指导开发者如何基于 ChatGPT 搭建完整的智能问答系统。 使用 ChatGPT 不仅仅是一个单一的 Prompt 或单一的模型调用,本课程将分享使用 LLM 构建复杂应用的最佳实践。 本课程以构建客服助手为例,…...
BJT晶体管
BJT晶体管也叫双极结型三极管,主要有PNP、NPN型两种,符号如下: 中间的是基极(最薄,用于控制),带箭头的是发射极(自由电子浓度高),剩下的就是集电极࿰…...
ORACLE中SQL运算符的优先级
SQL运算符优先级: 注: 1、可以使用括号改变优先级顺序 2、可以看出OR的优先级最低,算术运算符的优先级最高 另:操作符优先级 * / - 1、乘除的优先级高于加减; 2、同一优先级运算符从左向右执行; 3、括号内的…...
springboot和vue:十一、Axios网络请求的安装引入与使用、跨域问题解决(CORS)
Axios简介与安装 Axios是一个基于promise的网络请求库,作用于node.js和浏览器中Axios在浏览器端使用XMLHttpRequests发送网络请求,并自动完成json数据的转换安装:npm install axios官方文档:https://www.axios-http.cn/ Axios基…...
外汇天眼:真实记录,投资者在盗版MT4平台SCE Group上做交易的经历!
外汇市场是全球最大的金融市场,比起其他市场有着更多天然的优势,但也因为资讯的不对等,导致很多人上当受骗。而在外汇市场上最常见的骗局之一,就是黑平台使用盗版MT4/5交易软件,因为截至目前MT4/5仍是外汇市场交易使用…...
FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制
FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制 FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制安装软件:Screen Capturer Recorder查看可用设备名字音视频录制录制视频(默认参数)录制声音(默认参数&am…...
html 笔记:CSS
1 什么是CSS CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素样式通常存储在样式表中 1.1 css的语法格式 1.1.1 选择器种类 HTML选择器: 重新定义HTML的某种标签的显示格式id选择器 对于HTML文档中的某个标签,定义它的显示格式…...
【LeetCode - 每日一题】901. 股票价格跨度(23.10.07)
901. 股票价格跨度 题意 设计一个数据结构返回股票当日价格的跨度(必须是当日开始的) 解法 暴力 优化 一开始没理解题意,以为是求第 i 天及以前,小于等于 prices[i] 的最大连续子串的长度。后来才发现,这个最大连…...
第二证券:突发!A股T+0?刚刚,紧急回应!
沪深生意所急迫回应 6日,商场传出一个消息,传延伸A股生意时刻和部分票可日内T0一次。一个版本是提早至9点,然后下午延伸至15:30,另一个版本是上午推延至12点,下午延伸至16:00。 7日࿰…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...

