虚表

2023-11-18

虚表(虚函数表)

        C++中,一个类存在虚函数,那么编译器就会为这个类生成一个虚函数表,在虚函数表里存放的是这个类所有虚函数的地址(虚表从属于类)。编译器会为包含虚函数的类加上一个成员变量,该成员变量是一个指向虚函数表的指针,因此虚表指针是一个成员属性,也就是说,如果一个类含有虚表,那么类的每个对象都含有虚表指针。当生成类对象的时候,编译器会自动的将类对象的前四个字节设置为虚表的地址(不一定哦,但是大部分是),而这四个字节就可以看作是一个指向虚函数表的指针。

        虚函数表属于类,类的所有对象共享这个类的虚函数表
        虚函数表存储在只读数据段(.rodata),也就是说虚函数表在编译阶段就已经形成了,虚函数表指针是在构造函数中赋值的。
注意
        1) 派生类对象有两部分组成,一部分是从父类中继承到的成员(包括虚表指针),另一部分就是子类自己的成员
        3) 虚函数表本质是一个存放虚函数指针的指针数组,这个数组最后面放了一个nullptr
        4) 派生类的虚表的生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中的某个虚函数,用派生类自己的虚函数覆盖虚表中的基类的虚函数 c.派生类自己新增的虚函数按其在派生类中的声明次序依次增加到派生类虚表的最后
        5) 虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样都是存在于代码段中的,只是它的指针又存到了虚表中。对象中存的不是虚表,存的是虚表的指针。虚表也是存在于数据段的。
        6) 满足多态的的函数调用是在运行时确定的(从对象中找到函数地址),而不满足多态的函数调用是编译时确定好的
        7) 多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表

动态绑定和静态绑定

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

单继承的无重写虚函数

那么派生类将会把自己的虚函数加入到基类虚表中,按照声明次序依次加入

单继承的有重写虚函数

派生类将其重写的函数地址覆盖到虚表指定的位置上

无重写的单继承的虚拟继承

基类打印自己得虚函数,派生类只打印自己的,并不会打印基类的。
派生类对象结构为派生类虚函数指针、虚基类指针、派生类成员变量、基类虚函数指针、基类的成员变量
在这里插入图片描述
这里我们看到了虚基类指针其实就是指向当前位置与基类的偏移量

// 如果你想打印基类的方法
#include<iostream>
using namespace std;
class Base
{
public:
	Base()
	{
		b = 10;
	}
	virtual void fun0()
	{
		cout << "Base::fun0()" << endl;
	}
	virtual void fun1()
	{
		cout << "Base::fun1()" << endl;
	}
	virtual void fun2()
	{
		cout << "Base::fun2()" << endl;
	}
	int b;
};
class Derived :virtual public Base
{
public:
	Derived()
	{
		d = 20;
	}
	virtual void fun3()
	{
		cout << "Derived::fun3()" << endl;
	}
	virtual void fun4()
	{
		cout << "Derived::fun4()" << endl;
	}
	virtual void fun5()
	{
		cout << "Derived::fun5()" << endl;
	}
	int d;
};
typedef void(*vpf)();
void Printvpf()
{
	Base b;
	Derived d;
	//Base* p = &d;
	cout << "Base::vpf" << endl;
	vpf* n = (vpf*)*(int *)(&b);
	while (*n)
	{
		(*n)();
		n++;
	}
	cout << "Derived::vpf" << endl;
	vpf* pn = (vpf*)*(int *)(&d);
	while (*pn)
	{
		(*pn)();
		pn++;
	}
	cout << "Derived::Base vpf" << endl;
	vpf* pp = (vpf*)(*(int *)((char *)(&d) + 12));
	while (*pp)
	{
		(*pp)();
		pp++;
	}
	cout << sizeof(Base) << endl;
	cout << *((int *)(*(int*)((int*)(&d) + 1)) + 1) << endl;
	cout << sizeof(Derived) << endl;
}
int main()
{
	Printvpf();
	system("pause");
	return 0;
}

单继承的虚拟继承的有重写的虚表

基类只打印自己的,派生类将重写的不打印,只打印自己的
基类大小不变,派生类的大小和无重写几乎一样,不过多了一个用于区分对象的0,这个0加进了派生类数据成员和指向基类虚表的指针之间
在这里插入图片描述

#include<iostream>
using namespace std;
class Base
{
public:
	Base()
	{
		b = 10;
	}
	virtual void fun0()
	{
		cout << "Base::fun0()" << endl;
	}
	virtual void fun1()
	{
		cout << "Base::fun1()" << endl;
	}
	virtual void fun2()
	{
		cout << "Base::fun2()" << endl;
	}
	int b;
};
class Derived :virtual public Base
{
public:
	Derived()
	{
		d = 20;
	}
	virtual void fun0()
	{
		cout << "Derived::fun0()" << endl;
	}
	virtual void fun1()
	{
		cout << "Derived::fun1()" << endl;
	}
	virtual void fun5()
	{
		cout << "Derived::fun5()" << endl;
	}
	int d;
};
typedef void(*vpf)();
void Printvpf()
{
	Base b;
	Derived d;
	//Base* p = &d;
	cout << "Base::vpf" << endl;
	vpf* n = (vpf*)*(int *)(&b);
	while (*n)
	{
		(*n)();
		n++;
	}
	cout << "Derived::vpf" << endl;
	vpf* pn = (vpf*)*(int *)(&d);
	while (*pn)
	{
		(*pn)();
		pn++;
	}
	cout << sizeof(Base) << endl;
	cout << sizeof(Derived) << endl;
}
int main()
{
	Printvpf();
	system("pause");
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

虚表 的相关文章

  • X11 模式对话框

    如何使用 Xlib 在 X11 中创建模式对话框 模态对话框是一个位于应用程序其他窗口之上的窗口 就像瞬态窗口一样 并且拒绝将焦点给予应用程序的其他窗口 在 Windows 中 当试图从模态窗口夺取焦点时 模态也会通过闪 烁模态窗口的标题栏
  • 结构体如何存储在内存中?

    我有一个struct iof header在我的代码中 我确定它的宽度是 24 字节 我执行 sizeof iof header 它返回 32 字节宽 问题1为什么是 32 字节宽而不是 24 字节宽 问题2包括其成员在内 结构体如何存储在
  • 使用 Json.NET 序列化子类

    我正在尝试使用 Json NET 序列化子类 生成的 json 包含超类的序列化属性 但是not子类对象的属性 这似乎与我发现的一个问题有关这里就这样 https stackoverflow com q 5863496 498969 但必须
  • 将字符串作为 PChar 从 CSharp 传递到 Delphi DLL

    我正在尝试将字符串从 C 传递到 Delphi 构建的 DLL Delphi DLL 需要 PChar 这是Delphi导出 procedure DLL Message Location PChar AIntValue integer st
  • 最新 .Net MongoDb.Driver 的连接问题

    我创建了一个 MongoLab 沙箱数据库 我与 MongoChef 连接 效果很好 我通过 Nuget 安装了 MongoDB Driver 2 2 2 我编写了一些简单的 C 演示代码 但就是无法使其工作 连接字符串是直接从 Mongo
  • 如何在C中同时运行两个子进程?

    所以我开始学习并发编程 但由于某种原因我什至无法掌握基础知识 我有一个名为 fork c 的文件 其中包含一个 main 方法 在此方法中 我将 main 分叉两次 分别进入子进程 1 和 2 在孩子 1 中 我打印了字符 A 50 次 在
  • Monotouch全局异常处理

    我在野外发现了一只令人讨厌的虫子 但我无法确定它的具体情况 有没有办法拥有全局 Try Catch 块 或者有办法处理 Monotouch 中未处理的任何异常 我可以包起来吗UIApplication Main args 在 try cat
  • Visual Studio 中列表框的上移、下移按钮[重复]

    这个问题在这里已经有答案了 我正在尝试制作一个上移按钮和一个下移按钮 以移动 Microsoft Visual Studio 2012 中列表框中的选定项目 我已经在 WDF jquery winforms 和其他一些表单中看到了其他示例
  • 调用异步方法在视图模型的构造函数中加载数据有警告

    我的视图包含一个 ListView 它显示来自互联网的一些数据 我创建一个异步方法来加载数据并在我的视图模型的构造函数中调用该方法 它有一个警告提示我现在使用await关键字 还有其他解决方案可以在构造函数中异步加载数据吗 有几种可以应用的
  • 处理“未找到细胞”。 Excel 中的错误

    我正在使用 Excel VSTO 应用程序并使用以下代码在工作表中查找错误单元格 Excel Range rngTemp Excel Range rngErrorRange Excel Worksheet Sheet1 Excel Work
  • 我在使用 ado.net 时收到错误 Argument 2 may not be pass with ref keywords

    int t 0 cmd Parameters AddWithValue Res ref t 我在第二行收到错误 参数 2 不能与 ref 关键字一起传递 您只能通过引用传递参数ref if the 范围 is a ref参数也是如此 Add
  • 如何从外语线程调用Python函数(C++)

    我正在开发一个程序 使用 DirectShow 来抓取音频数据 媒体文件 DirectShow 使用线程将音频数据传递给回调 我的程序中的函数 然后我让该回调函数调用另一个函数 Python 中的函数 我使用 Boost Python 来包
  • 如何在 C# 中更改公共 IP 地址

    我正在创建一个 C winform 应用程序 我想在其中更改公共 IP 地址 而不是像 Hotspot Shield ZenMate OpenVPN 等那样更改 IPv4 地址 我已经检查了以下链接 但没有找到足够的帮助 所以我发布了这个问
  • Gremlin.net 文本包含等效项

    我正在使用 Gremlin net 库连接到 janus 图形服务器 我使用 cassandra 和弹性搜索进行数据存储和索引 在我使用的 gremlin 语言和 gremlin 控制台中文本包含在属性的文本中进行搜索 我正在使用混合索引
  • 如何将System.Windows dll添加到Visual Studio 2010 Express?

    我正在开发一个小型应用程序C and VS2010 as IDE with NET框架4 我想用CaptureSource类以便从笔记本电脑的网络摄像头捕获视频 为此我需要添加一个命名空间System Windows DependencyO
  • 使用多态对象数组进行 JSON 反序列化

    我在涉及多态对象数组的 JSON 反序列化方面遇到问题 我已经尝试过记录的序列化解决方案here https stackoverflow com questions 5186973 json serialization of array w
  • 具有四个 && 的 LINQ Where 子句

    我正在尝试在Where 子句中创建一个带有4 个参数的LINQ 查询 这是一个 Windows 8 应用程序项目 我正在使用 SQLite 数据库 SQLite 实现 https github com praeclarum sqlite n
  • 如何在c#中创建多线程

    我需要监听机器中的所有串行端口 假设我的机器有 4 个串行端口 我必须创建 4 个线程并开始分别使用附加线程监听每个端口 我使用此代码来获取我的机器中的端口数量 private SerialPort comPort new SerialPo
  • 使用 CodeDOM 将程序集添加到 BuildManager 会导致间歇性错误

    我正在使用 CodeDOM 在运行时创建内存中程序集 如下所示 public Assembly Compile CodeCompileUnit targetUnit string path Path GetDirectoryName new
  • 实体框架代码首次日期字段创建

    我正在使用实体框架代码优先方法来创建我的数据库表 下面的代码 创建一个DATETIME数据库中的列 但我想创建一个DATE柱子 DataType DataType Date DisplayFormatAttribute ApplyForma

随机推荐

  • Java会话技术

    会话技术 会话指的是客户端浏览器和服务端之间的度偶次请求和响应 当打开浏览器 访问网站地址后 会话开始 当关闭浏览器 或者到了过期时间 会话结束 就像打电话只要不挂电话就是一次会话 会话过程中产生的数据可以通过会话技术Cookie Sess
  • 抖音Scheme全网最全,startActivity地址

    例子 部分参数可省略 跳转主页并关注 snssdk1128 user profile 72673737181 refer web gd label click wap profile bottom type need follow need
  • oracle值相等时排序规则,Oracle中排序列中值相同引发的问题

    有个表 表名是Policy 有三个字段 a b c 这个表有两行 c列中的数据始终为0 我运行一下的sql语句 这个问题在Oracle的新闻中心被提出了一段时间 有个表 表名是Policy 有三个字段 a b c 这个表有两行 c列中的数据
  • vue element ui table 过滤功能实现

    Element UI代码
  • python中的list如何深度拷贝

    在 Python 中 可以使用 copy 模块中的 deepcopy 函数来进行深度拷贝 示例 import copy original list 1 2 3 4 5 6 7 8 9 copied list copy deepcopy or
  • c++全局变量的使用方式

    c 全局变量的使用方式 在cpp文件中定义 在头文件中定义 在cpp文件中定义 主函数中的使用 extern int aa extern int bb 在头文件中定义 在主函数中 在 h文件中声明 需要加extern关键字 在 cpp文件中
  • Linux环境下gcc编译链接库-lz -lrt -lm -lc都是什么库?

    编译链接库 lz lrt lm lc都是什么库 lz 压缩库 Z lrt 实时库 real time shm open系列 lm 数学库 math lc 标准C库 C lib dl 是显式加载动态库的动态函数库
  • Linux下ImageNet2012数据集下载及其配置

    简明扼要 一 训练集下载 137G http www image net org challenges LSVRC 2012 nnoupb ILSVRC2012 img train tar 验证集下载 http www image net
  • FCN模型训练中遇到的困难

    FCN模型训练中遇到的困难 标签 深度学习FCN神经网络caffe 2017 02 24 10 54 2675人阅读 评论 6 收藏 举报 分类 深度学习 18 版权声明 本文为博主原创文章 未经博主允许不得转载 前前后后大概忙了3个月了
  • 嵌入式数据库sqlite3【进阶篇】-子句和函数的使用,小白一文入门

    更多信息请关注公众号 一口Linux 在 嵌入式数据库sqlite3 基础篇 基本命令操作 小白一看就懂 一文中讲解了如何实现sqlite3的基本操作增删改查 本文介绍一些其他复杂一点的操作 比如where order by having
  • 基于内容的视频信息检索系统

    基于内容的视频信息检索系统 汪志强 江西财经大学信息管理学院 09信息管理与信息系统2班 摘 要 本文从基于内容的视频信息检索技术的发展历史出发 对基于内容的视频检索系统的技术要点及主要的功能模块进行了讨论和分析 并说明了当今技术存在的缺陷
  • MFC Windows 程序设计(一)-程序员的解放

    MFC Windows 程序设计 一 程序员的解放 程序之美 很久很久以前 程序员是一个很辛苦的工作 因为那时候大多数的软件都是用C语言编写的 Microsoft Visual Basic还没有出现 更不要说现在的Java Android
  • Tomcat修改默认端口号

    1 背景 在默认情况下 tomcat的端口是8080 使用了两个tomcat 那么就需要修改其中的一个的端口号才能使得两个同时工作 2 方法 2 1改动一 那么 如何修改tomcat的端口号呢 首先到安装目录 或者解压目录 下找到conf文
  • VUE之Echarts图表x轴y轴提示文字过长处理为省略号

    只需对显示文字格式修改即可 yAxis type category axisLine show false 轴线 axisTick show false 去除刻度 axisLabel formatter function params co
  • silk lobe资源公众号_资源合集

    11 月 十一月 iOS内置韩文字体 Apple SD Gothic Neo 锤子 黑 Smartisan 与方正合作定制的UI黑体 Emoji 鸽了好久的可爱 Emoji 字体 移植到安卓手机 沙扬娜拉 岩田仿宋 复古聚珍仿宋风格 返璞归
  • chatgpt赋能python:Python如何优化中文SEO

    Python如何优化中文SEO Python 作为一种流行的编程语言 可以用来开发各种不同的应用程序 当涉及到网络营销和搜索引擎优化 SEO 时 Python的功能也非常有用 在本篇文章中 我们将介绍如何使用Python来优化中文SEO 以
  • opencv显示对比

    在opencv中我们一般都要展示处理前后图像的对比 有时候我们会imshow两次来展示两张图片 那为什么我们不放在一个图片里呢 这样显然是更加优雅的模式 上代码 Mat combineImage Mat before Mat after a
  • Go语言实现Onvif客户端:4、配置网络信息

    Go语言实现Onvif客户端 4 配置网络信息 文章目录 Go语言实现Onvif客户端 4 配置网络信息 1 思路 2 代码 上一节获取到网络接口token后 就可进行一些网络配置了 这里我们暂时只实现进行ip地址的配置接口和封装 1 思路
  • 【SpringCloud】pom.xml文件解析

    本文档为本人学习交流所用 参考原文档 https www cnblogs com hoyong articles 13034270 html 1 pom xml是什么 pom是Project Object Model 项目对象模型 的缩写
  • 虚表

    虚表 虚函数表 C 中 一个类存在虚函数 那么编译器就会为这个类生成一个虚函数表 在虚函数表里存放的是这个类所有虚函数的地址 虚表从属于类 编译器会为包含虚函数的类加上一个成员变量 该成员变量是一个指向虚函数表的指针 因此虚表指针是一个成员