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

C++笔记 详解虚基表跟虚函数表

我来详细解析虚函数表(vtable)和虚基类表(vbtable)的底层实现。

1. 虚函数表 (vtable) 详解

基本结构和原理

cpp

#include <iostream>
using namespace std;class Base {
public:int base_data;Base() : base_data(100) {}virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }virtual ~Base() { cout << "Base::~Base" << endl; }
};class Derived : public Base {
public:int derived_data;Derived() : derived_data(200) {}void func1() override { cout << "Derived::func1" << endl; }void func2() override { cout << "Derived::func2" << endl; }virtual void func3() { cout << "Derived::func3" << endl; }
};// 手动模拟vtable的结构
struct VTable {void (*func1)(void*);void (*func2)(void*); void (*destructor)(void*);void (*func3)(void*);  // 派生类新增的虚函数
};

内存布局分析

cpp

void analyzeVTable() {Base base;Derived derived;cout << "=== 对象大小分析 ===" << endl;cout << "Base大小: " << sizeof(base) << " (数据:" << sizeof(int) << " + vptr:" << sizeof(void*) << " + 对齐)" << endl;cout << "Derived大小: " << sizeof(derived) << endl;cout << "\n=== 内存地址分析 ===" << endl;cout << "Base对象地址: " << &base << endl;cout << "Base数据地址: " << &base.base_data << endl;cout << "Derived对象地址: " << &derived << endl; cout << "Base部分数据: " << &derived.base_data << endl;cout << "Derived数据: " << &derived.derived_data << endl;// 获取vptr(平台相关代码)void** base_vptr = *(void***)&base;void** derived_vptr = *(void***)&derived;cout << "\n=== vtable地址 ===" << endl;cout << "Base vtable: " << base_vptr << endl;cout << "Derived vtable: " << derived_vptr << endl;
}

vtable的创建过程

cpp

/*
Base的vtable:
[0] → Base::func1()地址
[1] → Base::func2()地址  
[2] → Base::~Base()地址Derived的vtable:
[0] → Derived::func1()地址  // 重写
[1] → Derived::func2()地址  // 重写
[2] → Derived::~Derived()地址 // 重写
[3] → Derived::func3()地址  // 新增
*/

2. 虚基类表 (vbtable) 详解

基本结构和原理

cpp

#include <iostream>
using namespace std;class VirtualBase {
public:int vb_data;VirtualBase() : vb_data(999) {}
};class A : virtual public VirtualBase {
public:int a_data;A() : a_data(100) {}
};class B : virtual public VirtualBase {
public:int b_data; B() : b_data(200) {}
};class C : public A, public B {
public:int c_data;C() : c_data(300) {}
};// 手动模拟虚基类表
struct VBTable {int offset_to_top;      // 到对象顶部的偏移量int offset_to_vbase;    // 到虚基类的偏移量
};

内存布局分析

cpp

void analyzeVBTable() {C c_obj;cout << "=== 对象大小和地址 ===" << endl;cout << "C对象大小: " << sizeof(c_obj) << endl;cout << "C对象地址: " << &c_obj << endl;A* a_ptr = &c_obj;B* b_ptr = &c_obj;VirtualBase* vb_ptr = &c_obj;cout << "A* 视角: " << a_ptr << endl;cout << "B* 视角: " << b_ptr << endl; cout << "VirtualBase* 视角: " << vb_ptr << endl;cout << "\n=== 偏移量计算 ===" << endl;cout << "A到C的偏移: " << (char*)&c_obj - (char*)a_ptr << endl;cout << "B到C的偏移: " << (char*)&c_obj - (char*)b_ptr << endl;cout << "VirtualBase到C的偏移: " << (char*)&c_obj - (char*)vb_ptr << endl;// 模拟编译器如何通过vbtable找到虚基类cout << "\n=== 虚基类查找模拟 ===" << endl;cout << "从A找到VirtualBase: " << (char*)vb_ptr - (char*)a_ptr << endl;cout << "从B找到VirtualBase: " << (char*)vb_ptr - (char*)b_ptr << endl;
}

3. 底层实现对比

编译器生成的代码模拟

cpp

// 模拟编译器如何实现虚函数调用
void simulatedVirtualCall(Base* obj) {// 实际编译器生成的代码类似:void** vptr = *(void***)obj;        // 获取vptrvoid (*func)(void*) = (void(*)(void*))vptr[0];  // 获取第一个虚函数func(obj);                          // 调用函数
}// 模拟编译器如何访问虚基类成员
void simulatedVBaseAccess(A* a_ptr) {// 实际编译器生成的代码类似:void** vbptr = *(void***)a_ptr;     // 获取vbptrint offset_to_vbase = *(int*)((char*)vbptr + sizeof(int)); // 从vbtable取偏移量VirtualBase* vb_ptr = (VirtualBase*)((char*)a_ptr + offset_to_vbase);int data = vb_ptr->vb_data;         // 访问虚基类成员
}

4. 完整的内存布局示例

cpp

class Base {
public:int base_data;virtual void vfunc1() {}virtual void vfunc2() {}
};class VirtualBase {
public:int vb_data;
};class Derived : public Base, virtual public VirtualBase {
public:int derived_data;virtual void vfunc1() override {}virtual void vfunc3() {}
};/*
Derived对象的完整内存布局(典型实现):[0] vptr_Base → Base的vtable
[8] base_data
[12] derived_data  
[16] vptr_Derived → Derived的vbtable
[24] vb_dataBase的vtable:
[0] Derived::vfunc1()地址
[1] Base::vfunc2()地址
[2] 析构函数地址Derived的vbtable:  
[0] 到对象顶部的偏移量
[1] 到VirtualBase的偏移量
*/

5. 实际编译器输出分析

cpp

#include <iostream>
using namespace std;// 简单的测试类
class TestBase {
public:virtual void func() {}
};class TestVBase {
public:int data;
};class TestDerived : public TestBase, virtual public TestVBase {
public:virtual void func() override {}
};void testMemoryLayout() {TestDerived obj;cout << "=== 实际内存分析 ===" << endl;cout << "TestBase大小: " << sizeof(TestBase) << endl;cout << "TestVBase大小: " << sizeof(TestVBase) << endl; cout << "TestDerived大小: " << sizeof(TestDerived) << endl;cout << "对象地址: " << &obj << endl;cout << "TestBase部分: " << static_cast<TestBase*>(&obj) << endl;cout << "TestVBase部分: " << static_cast<TestVBase*>(&obj) << endl;// 使用gcc的扩展来直接查看vtable(如果支持)// void** vptr = (void**)*(void**)&obj;// cout << "vtable[0]: " << vptr[0] << endl;
}int main() {analyzeVTable();cout << "\n" << string(50, '=') << "\n" << endl;analyzeVBTable();cout << "\n" << string(50, '=') << "\n" << endl;testMemoryLayout();return 0;
}

6. 关键区别总结

特性虚函数表 (vtable)虚基类表 (vbtable)
目的实现运行时多态解决菱形继承问题
存储内容函数指针数组偏移量数组
指针位置对象开头(vptr)虚继承类的子对象内(vbptr)
访问方式间接函数调用地址计算+直接访问
性能开销一次间接调用地址计算+内存访问

7. 实际应用建议

cpp

// 好的设计:合理使用虚继承
class Interface {
public:virtual void operation() = 0;virtual ~Interface() = default;
};class BaseImpl : virtual public Interface {// 共享实现
};class ExtendedImpl : public BaseImpl {// 扩展功能
};// 避免过度使用虚继承,因为:
// 1. 增加内存开销(vbptr + vbtable)
// 2. 降低访问速度(需要偏移量计算)
// 3. 构造函数调用复杂化
http://www.dtcms.com/a/605130.html

相关文章:

  • 【开源-AgentRL】创新强化学习 多项任务超闭源模型
  • 渝水区城乡建设局网站有哪些wordpress博客
  • 龙岩网站推广软件wordpress文章图片粘贴固定大小
  • 物联网运维中的多模态数据融合与智能决策优化技术
  • lora学习
  • DR模式 LVS负载均衡群集
  • 【计算思维】蓝桥杯STEMA 科技素养考试真题及解析 C
  • openGauss 数据库快速上手评测:从 Docker 安装到SQL 实战
  • ffmpeg离线安装到服务器:解决conda/sudo/无法安装的通用方案
  • 力扣--两数之和(Java)
  • wordpress翻译公司网站吕梁网站制作
  • Lanelet2 OSM数据格式详解
  • 分布式系统保证数据强一致性的示例
  • Spring Boot性能提升的核武器,速度提升500%!
  • SOLIDWORKS 2025设计效率的大幅提高
  • 比标准Json库好用——json-iterator
  • 汇编语言编译器的作用 | 探讨汇编编译器的工作原理和实际应用
  • C语言编译器下载地址与安装指南
  • kanass实战教程系列(4) - 产品经理如何使用kanass有效管理需求
  • RLS(递归最小二乘)算法详解
  • 红色好看的网站济南网络推广软件公司
  • mvcc 简介
  • UniApp 商品分类左右联动技术文档
  • pytest 入门指南:Python 测试框架从零到一(2025 实战版)
  • SpringBoot教程(三十三)| SpringBoot集成MinIO
  • 【开题答辩全过程】以 基于.NET MVC的线上鞋服交易系统设计与实现为例,包含答辩的问题和答案
  • MySQL 全体系深度解析(存储引擎、事务、日志、MVCC、锁、索引、执行计划、复制、调优)
  • SpringMVC基础教程(1)--MVC/DispathcerServlet
  • 在streampark运行paimon-flink-action-1.20.0.jar
  • AI得贤面试智能体:重构企业招聘新范式