C++对象模型一图通:虚函数 vs 虚继承(vptr / vbptr 全部讲透)
一句话核心总结
虚函数:解决“调用哪个函数”(多态)
虚继承:解决“数据保留几份”(菱形继承)
👉 本质统一:
都是通过“指针间接层”来解决问题
一、虚函数(virtual function)
解决什么问题?
👉运行时多态(动态绑定)
示例
class Base { public: virtual void show() { std::cout << "Base\n"; } }; class Derived : public Base { public: void show() override { std::cout << "Derived\n"; } }; Base* p = new Derived(); p->show(); // 👉 输出 Derived底层原理
对象中隐藏一个指针:
vptr(虚函数指针)结构:
对象: [ vptr ] → vtable(虚函数表) [ 成员变量... ]调用流程
p->show() → 找 vptr → 找 vtable → 找函数地址 → 调用本质总结
虚函数 = 用 vptr 找函数地址,实现运行时多态二、虚继承(virtual inheritance)
解决什么问题?
👉菱形继承中的数据重复问题
菱形继承问题
class A { public: int x; }; class B : public A {}; class C : public A {}; class D : public B, public C {};问题
D d; d.x; // ❌ 二义性👉 因为:
D 中有两份 A: B::A::x C::A::x三、虚继承登场
class B : virtual public A {}; class C : virtual public A {};✅ 结果
D 中只有一份 A四、vbptr 是什么?
定义
vbptr = virtual base pointer(虚基类指针)作用
用来找到“虚基类 A 在内存中的位置”内存结构(核心)
D: ├─ B部分 │ └─ vbptr → 虚基类表(记录 A 偏移) │ ├─ C部分 │ └─ vbptr → 虚基类表(记录 A 偏移) │ └─ A(唯一一份) └─ x访问流程
d.B::x实际:
1. 进入 B 2. 找 vbptr 3. 查表 → 得到 A 的位置 4. 跳到 A 5. 访问 x五、关键结论(必须记住)
虚继承不是“复制 A” 而是“共享 A”本质一句话
B 和 C 并不拥有 A 而是共同指向同一份 A六、vptr vs vbptr 对比
| 对比 | vptr | vbptr |
|---|---|---|
| 全称 | virtual table pointer | virtual base pointer |
| 作用 | 找函数 | 找数据 |
| 解决问题 | 多态 | 菱形继承 |
| 指向 | vtable | 虚基类偏移表 |
| 是否常用 | 高频 | 较少 |
路径对比
虚函数
对象 → vptr → vtable → 函数虚继承
对象 → vbptr → 偏移表 → A → 数据七、统一理解(架构级)
C++对象模型的核心思想: 通过“指针间接层” 来解耦结构与行为三大核心指针
this → 当前对象 vptr → 找函数(多态) vbptr → 找数据(虚继承)八、常见误区
❌ 误区1
virtual 是一个东西👉 ❌ 错:
virtual function ≠ virtual inheritance❌ 误区2
虚继承和多态有关👉 ❌ 完全无关
❌ 误区3
vbptr 指向 A👉 ❌ 错:
vbptr → 指向“偏移表” → 表里记录 A 的位置九、工程建议
✅ 虚函数
- ✔ 高频使用
- ✔ 面向接口 / 多态设计
⚠️ 虚继承
- ⚠ 只在菱形继承使用
- ⚠ 会增加对象复杂度
最终总结
虚函数: → 用 vptr 找函数(行为多态) 虚继承: → 用 vbptr 找数据(共享基类) 本质: → 用“间接层”解决复杂性进阶思考(面试加分)
👉 一个对象里:
- vptr 有几个?
- vbptr 有几个?
- 多继承下 this 如何调整?
这些就是 C++对象模型的高级区
