多重继承下虚拟函数表构建与动态绑定
- 虚拟函数表构建:
- 在多重继承中,一个派生类可能从多个基类继承虚函数。每个基类的虚函数表会在派生类对象内存布局中占有一定位置。例如,若派生类
Derived
从基类Base1
和Base2
继承,Derived
对象内存布局中会有对应Base1
和Base2
的虚函数表指针(通常在对象头部)。
- 派生类重写的虚函数会在相应基类虚函数表中替换掉基类版本的函数指针。
- 动态绑定实现:
- 当通过指针或引用调用虚函数时,首先根据对象指针获取对应的虚函数表指针。
- 然后根据虚函数在虚函数表中的偏移量找到具体的函数地址并调用。例如,
Base1* ptr = &derivedObj; ptr->virtualFunc();
,先通过ptr
找到derivedObj
中对应Base1
的虚函数表指针,再根据virtualFunc
在虚函数表中的偏移找到函数地址调用。
菱形继承下虚拟函数表构建与动态绑定
- 虚拟函数表构建:
- 在菱形继承体系(如
A
为基类,B
和C
继承自A
,D
多重继承自B
和C
)中,为解决数据冗余和二义性问题,引入虚继承。
B
和C
继承A
时采用虚继承,这样B
和C
对象内存布局中会有一个虚基类指针,指向共享的A
子对象。
D
对象内存布局中,会有多个虚函数表指针,分别对应B
和C
的虚函数表。虚函数表中,对于从A
继承的虚函数,会指向D
中重写的版本(如果有重写),否则指向A
中的实现。
- 动态绑定实现:
- 当
D
对象调用func()
时,D
对象指针首先找到对应的虚函数表指针(根据调用的是从B
继承的接口还是从C
继承的接口确定使用哪个虚函数表)。
- 然后在虚函数表中根据
func()
的偏移量找到函数地址。由于虚继承,B
和C
共享A
子对象,所以最终会调用到正确的func()
实现(无论是A
的版本还是D
重写的版本)。
内存布局示意图
// 菱形继承体系内存布局示意(简化,仅考虑虚函数表指针和虚基类指针)
// 假设类A大小为4字节(仅虚函数表指针),类B和C在虚继承下大小为8字节(虚函数表指针 + 虚基类指针),类D大小为16字节(两个虚函数表指针 + 两个虚基类指针)
// 内存地址从左到右增加
// D对象内存布局
+----------------+
| vptr for B | // 指向B的虚函数表,包含func()等虚函数指针
+----------------+
| vptr for C | // 指向C的虚函数表,包含func()等虚函数指针
+----------------+
| vbptr for B | // 虚基类指针,指向共享的A子对象
+----------------+
| vbptr for C | // 虚基类指针,指向共享的A子对象
+----------------+
// B虚函数表布局
+----------------+
| func() address | // 可能指向D重写的func(),若未重写则指向A的func()
+----------------+
// C虚函数表布局
+----------------+
| func() address | // 可能指向D重写的func(),若未重写则指向A的func()
+----------------+
// A子对象布局(共享)
+----------------+
| vptr for A | // A的虚函数表指针,指向A的虚函数表
+----------------+