了解多重继承中的虚表

2023-12-27

我有一个实现两个抽象类的类,如下所示。没有虚拟继承。无数据成员。

class IFace1 {
public:
    virtual void fcn(int abc) = 0;
};

class IFace2 {
public:
    virtual void fcn1(int abc) = 0;
};

class RealClass: public IFace1, public IFace2 {
public:
    void fcn(int a) {
    }

    void fcn1(int a) {
   }
};

我发现 RealClass 的 vtable 和对象内存布局如下所示。

Vtable for RealClass
RealClass::_ZTV9RealClass: 7u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI9RealClass)
16    (int (*)(...))RealClass::fcn
24    (int (*)(...))RealClass::fcn1
32    (int (*)(...))-8
40    (int (*)(...))(& _ZTI9RealClass)
48    (int (*)(...))RealClass::_ZThn8_N9RealClass4fcn1Ei

Class RealClass
    size=16 align=8
    base size=16 base align=8
RealClass (0x2af836d010e0) 0
    vptr=((& RealClass::_ZTV9RealClass) + 16u)
    IFace1 (0x2af836cfa5a0) 0 nearly-empty
        primary-for RealClass (0x2af836d010e0)
    IFace2 (0x2af836cfa600) 8 nearly-empty
        vptr=((& RealClass::_ZTV9RealClass) + 48u)

我对此很困惑。什么是 RealClass::_ZThn8_N9RealClass4fcn1Ei?为什么IFace2的vptr指向那个?当我从 IFace2 * 调用 fcn1 时会发生什么?程序如何在RealClass的Vtable中找到RealClass::fcn1?我想它以某种方式需要使用 IFace2 vptr,但不清楚具体如何使用。


警告:下面的大部分内容当然是依赖于实现和平台的并进行了简化。我将按照我在您的示例中看到的实现方式进行操作 - 可能是 GCC,64 位。


首先,虚拟类实例的契约是什么?例如。如果你有一个变量IFace1* obj:

  • 有一个指向虚拟表的指针obj+0.
  • 任何成员数据字段将继续obj+8 (sizeof(void*)).
  • 虚拟表包含一条记录,该记录指向void fcn(int) at vtbl+0.
  • 表中还有一个指针typeinfo班级的vtbl-8(由dynamic_cast等)和“到基数的偏移量" at vtbl-16.

任何看到类型变量的函数IFace1*可以相信这是真的。同样对于IFace2*.

  • 如果他们想调用虚函数void fcn(int),他们看着obj+0获取虚函数表,然后在vtbl+0并拨打在那里找到的地址。this被设定为obj.
  • 如果他们想要访问成员字段(通过他们自己,例如,如果该字段具有公共访问权限,或者如果有内联访问器),他们只需在其地址处读/写该成员obj+xxx.
  • 如果他们想查看他们真正拥有的类型,他们会减去vtbl-16从地址到他们的对象,然后看看typeinfo基础对象引用的虚函数表的指针。

现在,编译器如何满足具有多重继承的类的这些要求?

1)首先它需要为自己生成结构。虚拟表指针必须位于obj+0,就是这样。桌子会是什么样子?嗯,到基数的偏移量为0,显然,typeinfo数据和指向它的指针很容易生成,然后是第一个虚拟函数和第二个,没有什么特别的。任何知道定义的人RealClass可以进行相同的计算,因此他们知道在哪里可以找到虚函数表等中的函数。

2)然后它就可以让RealClass被传递为IFace1。所以它需要有一个指向虚拟表的指针IFace1格式化对象中的某处,那么虚拟表必须有该记录void fcn(int).

编译器很聪明,发现它可以重用它生成的第一个虚拟表,因为它符合这些要求。如果有任何成员字段,它们将存储在指向虚拟表的第一个指针之后,因此即使是它们也可以像派生类是基类一样简单地访问。到目前为止,一切都很好。

3)最后,如何处理该对象以便其他人能够将其用作IFace2?已经创建的一个 vtable 不能再使用了,因为IFace2需要它的void fcn1(int)处于vtbl+0.

因此,创建了另一个虚拟表,即您在转储中紧随第一个虚拟表之后看到的虚拟表,并且指向它的指针存储在RealClass在下一个可用的地方。第二个表需要有到基数的偏移量设置为 -8,因为真实对象从偏移量 -8 开始。它只包含指向该指针的指针IFace2虚函数,void fcn1(int).

对象中的虚拟指针(位于偏移量处)obj+8) 之后将跟随以下的任何成员数据字段IFace2,这样当给定指向该接口的指针时,任何继承或内联函数都可以再次工作。


好的,现在别人怎么能打电话给fcn1() from IFace2?那是什么non-virtual thunk to RealClass::fcn1(int)?

如果你通过了你的RealClass*指向一个陌生函数的指针,该函数需要IFace2*,编译器将发出代码将指针增加 8(或者无论多大)sizeof(void*) + sizeof(IFace1)是),这样函数就得到了以虚表指针开头的指针IFace2,然后是它的成员字段——正如我之前概述的合同中商定的那样。

当该函数想要调用时void IFace2::fcn1(int),它会查看虚拟表,转到该特定函数的记录(第一个也是唯一一个)并调用它,this设置为作为指针传递的地址IFace2.

这里出现了一个问题:如果有人调用这个实现的方法RealClass on a RealClass指针,this指向基数RealClass。与IFace1。但是如果它是由拥有指向的指针的人调用的IFace2界面,this相反,将 8 个(或任意多个)字节指向对象!

因此,编译器需要多次生成该函数来适应这一点,否则它无法正确访问成员字段和其他方法,因为它根据调用该方法的人而有所不同。

编译器通过创建隐藏的隐式小代码来优化它,而不是让代码真正重复两次。thunk函数代替,这只是

  1. 减少了this指针移动适当的量,
  2. 调用真正的方法,现在无论谁调用它都可以正常工作。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解多重继承中的虚表 的相关文章

  • Web 应用程序框架:C++ 与 Python

    作为一名程序员 我熟悉 Python 和 C 我正在考虑编写自己的简单 Web 应用程序 并且想知道哪种语言更适合服务器端 Web 开发 我正在寻找一些东西 它必须是直观的 我认识到 Wt 存在并且它遵循 Qt 的模型 我讨厌 Qt 的一件
  • 委托和接口如何互换使用?

    我可以使用接口方法代替委托吗 如何 我发现搜索接口方法比使用委托更快 我希望有一个简单的代码片段 理论上 可以通过包含单个方法的接口 例如 Java 没有委托 来完成委托完成的所有工作 然而 它使代码变得更加冗长并且没有带来什么好处 话又说
  • OpenGL,如何独立旋转对象?

    到目前为止我的代码 void display void glClear GL COLOR BUFFER BIT GL DEPTH BUFFER BIT Clear Screen And Depth Buffer glLoadIdentity
  • 以概率从列表中选择随机元素

    我有一个包含四个项目 A B C D 的列表 每个项目都有被选择的概率 例如 A 有 74 的机会被选中 B 15 C 7 D 4 我想创建一个函数 根据其概率随机选择一个项目 有什么帮助吗 为您的项目定义一个类 如下所示 class It
  • File.ReadAllLines 或流读取器

    我们可以使用以下方式读取文件StreamReader http msdn microsoft com en us library vstudio system io streamreader或通过使用File ReadAllLines ht
  • 如何获取 C# PriorityQueue 元素的优先级

    我正在初始化一个存储 XY 坐标的优先级队列 根据距原点的欧几里得距离确定优先级 我创建了一个自定义Comparer这使得它作为最大堆运行 PriorityQueue
  • 如何使用 C# 调用 REST API?

    这是我到目前为止的代码 public class Class1 private const string URL https sub domain com objects json api key 123 private const str
  • const_iterators 更快吗?

    我们的编码指南更喜欢const iterator 因为它们比正常的要快一点iterator 当您使用时 编译器似乎会优化代码const iterator 这真的正确吗 如果是的话 内部到底发生了什么使得const iterator快点 编辑
  • 泛型类上的 DebuggerDisplay

    我在应用时遇到问题DebuggerDisplay泛型类的属性 DebuggerDisplay foo class Foo DebuggerDisplay Bar t class Bar
  • 二元运算符重载、隐式类型转换

    class my bool private bool value public my bool bool value value value explicit operator bool return value friend my boo
  • 加载配置文件时发生错误:访问路径 c:\Program Files (x86)\... 被拒绝

    我有一个在 Windows 7 上使用 Visual Studio 2010 中的安装程序部署的应用程序 该程序在 Windows 7 和 XP 上部署并运行良好 但当我在 Windows 8 系统上部署它时 出现有关访问配置文件的错误 该
  • C++ fill() 与 uninitialized_fill()

    您好 我是初学者 我想知道容器的 fill 和 uninitialized fill 之间的区别 我在谷歌上进行了快速搜索 但没有得到很好的答案 有人可以帮助我吗 fill 将值 使用赋值运算符 分配给已构造的对象 uninitialize
  • 谷歌基准迭代的意义是什么?

    我正在使用 Google Benchmark 来测量某些代码的执行时间 例如 我编写了以下代码来测量其执行时间性能 include
  • 如何修改 edmx 的默认代码生成策略?

    我想修改默认的代码生成策略 该怎么做 我只是想修改类名 lt code Escape container gt to Entities并将默认连接字符串更改为name Default 我不想为该项目创建模板文件 我想编辑它以便它可以在全球范
  • 向窗口句柄发送消息

    我尝试使用 sendmessage 将消息从我的 C 应用程序传递到 C 我的c 代码是这样的 int tmain int argc TCHAR argv COPYDATASTRUCT cpd cpd dwData 0 LPCWSTR st
  • int 类型的构造函数

    考虑到成本 这些情况是否相同 case 1 int a 5 case 2 int a 5 case 3 int a a 5 这三种语法是不同的 请耐心等待 我使用用户定义类型而不是 int 稍后我将回到 int T a 5 Direct i
  • 具有两个表的谓词构建器

    A Party可以有一个或多个Contact对象 我想选择全部Parties谁的街道名称包含特定关键字 如果我只想搜索Party我可以使用下面的代码 但我如何扩展它来搜索Contact public IQueryable
  • 使用 /clr 或 clr:pure(cpprestsdk 又名 casablanca)编译时不支持互斥

    我创建一个CLR project in visual c with 64 bit配置 并尝试使用cpprestsdk aka casablanca 64bit 但是当我运行项目时 出现了错误 1 gt Build started Proje
  • 在 Visual Studio C++ 资源编辑器中导入 png 文件

    我希望能够在 Visual Studio 资源编辑器中导入 png 文件 以便能够在不同的其他项目中使用嵌入的资源 有解决办法吗 我知道它适用于位图 但我对 png 感兴趣 因为即使在较低格式 16x16 或 32x32 上也可以使用 透明
  • 当前线程中的单例

    我的单身人士如下 public class CurrentSingleton private static CurrentSingleton uniqueInstance null private static object syncRoo

随机推荐

  • Android Facebook 对话框

    我已将 Facebook 集成到我的应用程序中 但是 它不是在对话框中显示 而是全屏打开 我想知道是否有人知道如何将其更改为对话框 Facebook 处理程序类 Override public void onCreate Bundle sa
  • 无法连接到总线:没有这样的文件或目录

    我创建了一个 docker 16 4 容器并尝试在容器内运行 docker 继这篇文章之后 https www digitalocean com community tutorials how to install and use dock
  • 使用 MS 编译器的 std::cout 非常慢

    我正在打印多次计算迭代的进度 输出实际上是其中最慢的部分 但只有当我使用 Visual C 编译器时 MinGW 在同一系统上才能正常工作 考虑以下代码 include
  • 实现 HttpSessionListener

    我面临着一个类似的问题 https stackoverflow com questions 1439743 whats wrong with my listener in my web xml并了解到我的侦听器类将在读取 web xml 时
  • Python、SQLite3:当提交介入时游标返回重复项

    此 Python 代码创建一个表 向其中插入三行并迭代这些行 并在游标完全耗尽之前进行干预提交 为什么它返回五行而不是三行 如果删除干预提交 则返回的行数如预期为 3 或者是否预期提交 甚至不触及相关表 会使游标无效 Edit 添加了忘记的
  • jQuery 的 .isWindow 方法?

    我试图从 jQuery 的动画函数中了解我能做什么 但最终遇到了各种我不理解的内部函数 最终落在了 isWindow 上 isWindow 的代码检查对象是否具有该属性setInterval 否则返回 false 当然 任何对象都可以具有以
  • 如何仅对一个 .vue 文件禁用 vue/multi-word-component-names eslint 规则?

    我正在使用Vue ESLint 插件 https eslint vuejs org 它有一个规则不允许使用单个单词的组件名称 https eslint vuejs org rules multi word component names h
  • PHP upload_max_filesize

    我的 php 文件上传有问题 在我的 php ini 中 upload max filesize 设置为 4mb 当我尝试上传大于该文件的文件时 我从未像预期的那样收到 UPLOAD ERR INI SIZE 错误 但页面再次显示表单 但没
  • 适用于 Google BigQuery 标准 SQL 的任何 JDBC 驱动程序

    我需要 JDBC 驱动程序将我的应用程序连接到 Google BigQuery 我尝试了 CData JDBC 驱动程序 但它不支持所有类型的标准 SQL 查询 还有其他完整的选择吗 官方BigQuery 的 JDBC 驱动程序 https
  • 即复杂的子域cookie问题

    我所有的 cookie 在子域上都工作得很好 但如果子域中有 那么 cookie 根本不会被读取 也根本不会正确 以下是我的测试结果 它将自我解释 justlife demo com works fine just life demo co
  • 如何通过 Google Tag Manager for Next-Js 设置 Google Analytics?

    以前我使用react ga npm 模块在我的下一个js 应用程序中插入谷歌分析 就是这样 import ReactGA from react ga export const initGA gt ReactGA initialize UA
  • 当新子项添加到 Firebase 数据库时发送通知

    我在我的应用程序中使用 Firebase 数据库 当新订单添加到数据库 新子添加到数据库 时 我需要向管理员发送通知 我发现了一个名为 Firebase 云消息传递的东西 但我不知道如何使用它 有什么帮助吗 Firebase Cloud M
  • 使用动态参数查询 Diesel 表

    我开始考虑使用 Diesel 来查询数据库 我有一个类似于下面结构的表格 这只是一个玩具项目 旨在帮助我了解 Diesel 的工作原理 derive Queryable Insertable table name posts struct
  • ServletContextListener 严重:配置类 marktest.Config 的应用程序侦听器时出错

    我的 Java servlet 似乎抱怨它找不到包 marktest 中包含的文件 我使用 Eclipse Indigo 和 Tomcat7 进行开发 这是错误 SEVERE Error configuring application li
  • 选择除一个元素之外的所有主体

    我试图选择 jQuery 中除 this 或悬停的元素之外的所有主体元素 我试图让身体达到一定的不透明度 但 这个 是为了保持其不透明度 这是我的代码 content img mouseenter function this animate
  • 自定义形状旋转问题

    我正在尝试围绕其中心旋转自定义形状 但无法获得预期的结果 我想要的是 形状应绕其中心旋转而不移动自身 我的解决方案目前正在做的是围绕其中心旋转整个形状 每次旋转都会改变其位置 我有多个形状 所以我创建了一个类来封装形状及其在以下类中的转换
  • CSS:百分比最小高度元素嵌套在百分比最小高度元素中

    我想让 html body 和wrapper 元素的最小高度都为 100 以便覆盖整个查看窗口 但我发现我只能使 html 遵守此声明 html body wrapper min height 100 html border 2px red
  • 随机化两个值之间的矩阵元素,同时保持行和列总和固定 (MATLAB)

    我遇到了一些技术问题 但我觉得使用 MATLAB 强大的工具集应该可以实现 我拥有的是一个由 0 和 w 组成的随机 n n 矩阵 例如生成的 A w rand n n
  • 动态ul li添加滚动条

    我搜索了许多帖子和论坛 因为我认为这可能是一个基本的东西 但没有找到它 所以在这里询问 我想做的就是添加滚动条 如果高度超过一定限制 假设菜单项超过3 我创建了一个jsfiddlehttp jsfiddle net euSWB http j
  • 了解多重继承中的虚表

    我有一个实现两个抽象类的类 如下所示 没有虚拟继承 无数据成员 class IFace1 public virtual void fcn int abc 0 class IFace2 public virtual void fcn1 int