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

C++----验证派生类虚函数表的组成

1.先将基类中的虚表内容拷贝一份到派生类虚表中 2.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 3.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

上述前两点在上一篇博客已经写得非常清楚了,就是第三点,我们来验证一下:

class Person {
public:virtual void Buyticket(){cout << "全价" << endl;}virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:virtual void Buyticket(){cout << "半价" << endl;}virtual ~Student() { cout << "~Student()" << endl; }virtual void fun(){cout << "fun()" << endl;}
};void System(Person* p)
{p->Buyticket();
}int main()
{Student s;}

在监视窗口看一下s的构成:并没有发现fun的存在,那在内存窗口看一下

可以看到在nullptr前有一个四字节的数据,我们暂时猜测他是fun函数

接下来写段代码验证一下:查阅资料发现,vtable的类型为void*,也就是说虚函数表中存的函数指针的类型是void*,为什么呢?

1. vptr 本质是什么?

  • vptr(虚函数表指针):对象中隐藏的一个指针成员,指向 虚函数表(vtable)

  • vtable(虚函数表):一个 函数指针数组,数组里存放着所有虚函数的入口地址。


2. vtable 的存储形式

假设有类:

class Base {
public:virtual void f();virtual void g();
};

编译器会生成一个 vtable,大概长这样:

vtable (一个数组,存函数指针)
+----------------+
| &Base::f       |  // 函数地址
+----------------+
| &Base::g       |
+----------------+
| 0 (可能的结尾) |
+----------------+

注意:函数指针的类型取决于函数签名,可能是

void(*)(Base*)
int(*)(Base*, int)

等等,并不是统一的。


3. 为什么用 void* 来抽象?

  • C++ 里不同函数指针类型之间不能互相赋值。

  • 但 vtable 里存放的是一堆不同签名的函数指针(返回值不同、参数不同)。

  • 为了在编译器内部统一表示,vtable 通常被实现为一个 void* 数组,即 void*[]


4. 那么 vptr 呢?

  • vptr 就是 指向 vtable 数组开头的指针

  • 如果 vtable 是 void*[],那么 vptr 的类型就是 void**


5. 对应关系

  • vtable: void* vtable[](存放函数地址,编译器内部知道怎么解释)

  • vptr: void** vptr(存放在对象里,指向 vtable 的首元素)


6. 举个例子(伪代码)

对象内存布局大概是这样:

+--------------------+
| vptr (void**) ---> +--- vtable[0] (void*,函数指针)
| member1            |    vtable[1] (void*)
| member2            |    ...
+--------------------+

所以:

Student s;
void** vtable = *(void***)&s;  // 取出 vptr

其实就是:

  • &s → 得到对象地址

  • 强转为 (void***) → 把对象开头当成存了一个 void** 的地方

  • 解引用 → 得到 vptr(指向 vtable)

如何更好地理解void** vtable = *(void***)&s; 呢?举一个例子:

struct X {int a;   // 前4字节double b;
};X obj{42, 3.14};
int _a = *(int*)&obj;
cout<<_a;
//打印42

和这个例子是一样的,强制转换指针类型不是目的,利用指针类型解引用访问相应大小字节内容才是真!

typedef void(*Fun)(void*);  // 函数指针类型
//在C++11中可以使用 using Fun = void(*)(void*);int main()
{Student s;void** vtable = *(void***)&s;  // 拿到 vtable 指针Fun f = (Fun)vtable[2];  // 第 2 个函数指针(从 0 开始)(*f)(&s);//也可以写f(&s),编译器会自动解引用函数指针并跳转执行
}


文章转载自:

http://4Bt0UZrt.mftzm.cn
http://zkEOxzBn.mftzm.cn
http://iYkFliEW.mftzm.cn
http://CBgvPl1E.mftzm.cn
http://5BN3XyFR.mftzm.cn
http://xWHozuD7.mftzm.cn
http://UosouXgO.mftzm.cn
http://IbcwYYAK.mftzm.cn
http://fBFqpScZ.mftzm.cn
http://wdWbVa65.mftzm.cn
http://1gBMXD6z.mftzm.cn
http://hSEPN5XE.mftzm.cn
http://YYt9fgrt.mftzm.cn
http://4P7slPH5.mftzm.cn
http://pwGRpo6A.mftzm.cn
http://3xMW9piI.mftzm.cn
http://UamrV7jP.mftzm.cn
http://WdTNm0C9.mftzm.cn
http://lBWCc2S6.mftzm.cn
http://WwL4KeEd.mftzm.cn
http://U9pfEZld.mftzm.cn
http://VKeBMr7H.mftzm.cn
http://fIrilGv7.mftzm.cn
http://MhBwOasn.mftzm.cn
http://YfKnS2UP.mftzm.cn
http://KSm13MQE.mftzm.cn
http://mdY8PEDU.mftzm.cn
http://4BCbJMWL.mftzm.cn
http://Sb1evoSG.mftzm.cn
http://ZoIggwPa.mftzm.cn
http://www.dtcms.com/a/373475.html

相关文章:

  • moxa uport1150串口驱动ubantu20.04 5.15内核安装
  • 中州养老项目:登录功能项目鉴权
  • 2025年渗透测试面试题总结-58(题目+回答)
  • [Dify实战]插件编写- 如何让插件直接输出文件对象(支持 TXT、Excel 等)
  • StringBuilder类的数据结构和扩容方式解读
  • SQL 层面行转列
  • XR数字融合工作站赋能新能源汽车专业建设的创新路径
  • 大模型(LLM)安全保障机制(技术、标准、管理)
  • 【LeetCode】String相关算法练习
  • Redis基本数据类型
  • 深度学习(三):监督学习与无监督学习
  • crew AI笔记[5] - knowledge和memory特性详解
  • MyBatis多数据库支持:独立 XML 方案与单文件兼容方案的优劣势分析及选型建议
  • 安卓玩机工具----安卓“搞机工具箱”最新版 控制手机的玩机工具
  • 100、23种设计模式之适配器模式(9/23)
  • Docker网络模式解析
  • ARM处理器基础
  • TDengine 选择函数 First 用户手册
  • 9.8网编基础知识day1
  • 卷积神经网络(CNN):从图像识别原理到实战应用的深度解析
  • 【LeetCode - 每日1题】将整数转换为两个无零整数的和
  • 【超详细图文教程】2025年最新 Jmeter 详细安装教程及接口测试示例
  • MongoDB 常见错误解决方案:从连接失败到主从同步问题
  • Guava中常用的工具类
  • Entity Digital Sports 降低成本并快速扩展
  • 计算机毕业设计选题:基于Spark+Hadoop的健康饮食营养数据分析系统【源码+文档+调试】
  • Rust异步运行时最小实现 - extreme 分享
  • 内网穿透的应用-Navidrome与cpolar本地搭建跨网络访问的云音乐服务器
  • 金融量化指标--2Alpha 阿尔法
  • Qoder 完整使用指南