1.1 单继承下的虚函数表结构
单继承是最基础的继承形式,其虚函数表结构相对简单。我们通过以下示例分析:
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; // 基类 class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virtual void func2() { cout << "Base::func2()" << endl; } private: int _b; }; // 派生类(单继承Base) class Derive : public Base { public: // 重写Base的func1 virtual void func1() { cout << "Derive::func1()" << endl; } // 新增虚函数 virtual void func3() { cout << "Derive::func3()" << endl; } private: int _d; }; int main() { Base b; Derive d; return 0; }1.1.1 基类(Base)的虚函数表
Base类包含两个虚函数func1和func2,其虚函数表结构如下:
- 表中依次存储
func1和func2的函数指针 - 虚函数表位于代码段(常量区),与对象实例的内存空间分离
- Base 类的对象
b包含一个 vptr(4 字节 / 8 字节,取决于 32/64 位程序)和成员变量_b
通过 VS 的内存窗口观察Base对象的vptr指向的地址(虚函数表起始地址),可看到表中存储的两个函数指针,分别对应Base::func1和Base::func2。
1.1.2 派生类(Derive)的虚函数表
Derive 类继承自 Base 并做了两件事:重写func1、新增func3。其虚函数表有如下特点:
- 继承基类的虚函数表结构,重写的
func1会替换表中对应位置的函数指针(现在指向Derive::func1); - 未重写的
func2仍然指向Base::func2; - 新增的虚函数
func3被追加到虚函数表的末尾; - Derive 对象
d的内存布局为:vptr → 基类成员_b→ 派生类成员_d。
我们通过调试工具查看 Derive 对象的 vptr 指向的虚函数表,会发现前两个条目分别为Derive::func1和Base::func2,第三个条目为Derive::func3,完全符合上述结构。
1.2 单继承中虚函数表的访问机制
当使用基类指针指向派生类对象时,通过指针调用虚函数会触发动态绑定,其过程依赖虚函数表:
代码语言:javascript
AI代码解释
int main() { Base* ptr = new Derive(); ptr->func1(); // 调用Derive::func1() ptr->func2(); // 调用Base::func2() // ptr->func3(); // 编译错误:Base类无func3声明 delete ptr; return 0; }动态绑定的执行步骤:
- 从ptr指向的对象中取出vptr(此时指向 Derive 的虚函数表);
- 根据虚函数在表中的偏移量找到对应的函数指针(
func1在表中索引为 0,func2索引为 1); - 调用函数指针指向的实际函数。
注意:基类指针是无法直接调用派生类新增的虚函数的(如func3),因为基类中没有该函数的声明,编译器无法确定其在虚函数表中的偏移量。
1.3 单继承下的虚析构函数与虚函数表
析构函数是否为虚函数直接影响对象销毁时的行为。当基类析构函数为虚函数时,其派生类析构函数会自动成为虚函数,并被加入虚函数表:
代码语言:javascript
AI代码解释
class Base { public: virtual ~Base() { cout << "Base::~Base()" << endl; } // 虚析构 virtual void func() { cout << "Base::func()" << endl; } }; class Derive : public Base { public: ~Derive() { cout << "Derive::~Derive()" << endl; } // 自动为虚函数 }; int main() { Base* ptr = new Derive(); delete ptr; // 正确调用Derive::~Derive()和Base::~Base() return 0; }此时 Derive 的虚函数表中,析构函数的位置会替换基类析构函数的位置,确保delete基类指针时能调用派生类的析构函数,避免内存泄漏。
二、多继承下的虚函数表:结构与访问规则
多继承允许一个类同时继承多个基类,这会导致虚函数表结构变得复杂。根据基类是否包含虚函数,多继承的虚函数表可分为多种情况。
2.1 多继承(无虚函数覆盖)的虚函数表
当派生类继承多个基类且不重写任何虚函数时,每个基类的虚函数表会被独立保留,派生类新增的虚函数通常追加到第一个基类的虚函数表中。
代码语言:javascript
AI代码解释
class Base1 { public: virtual void func1() { cout << "Base1::func1()" << endl; } virtual void func2() { cout << "Base1::func2()" << endl; } int _b1; }; class Base2 { public: virtual void func3() { cout << "Base2::func3()" << endl; } virtual void func4() { cout << "Base2::func4()" << endl; } int _b2; }; // 多继承Base1和Base2 class Derive : public Base1, public Base2 { public: virtual void func5() { cout << "Derive::func5()" << endl; } // 新增虚函数 int _d; };2.1.1 内存布局分析
Derive 对象的内存结构包含:
- 第一个基类 Base1 的部分:vptr1(指向 Base1 的虚函数表)→
_b1 - 第二个基类 Base2 的部分:vptr2(指向 Base2 的虚函数表)→
_b2 - 派生类自身部分:
_d
对应的虚函数表结构:
- vptr1 指向的表:
Base1::func1→Base1::func2→Derive::func5(新增函数追加到第一个表) - vptr2 指向的表:
Base2::func3→Base2::func4
验证代码:通过打印虚函数表内容可直观看到这一结构:
代码语言:javascript
AI代码解释
// 打印虚函数表(简化实现,仅用于演示) typedef void(*VFPTR)(); // 定义虚函数指针类型 void PrintVTable(VFPTR* vtable) { cout << "虚函数表地址:" << vtable << endl; for (int i = 0; vtable[i] != nullptr; ++i) { cout << "第" << i << "个虚函数地址:" << vtable[i]; VFPTR f = vtable[i]; f(); // 调用虚函数 } cout << endl; } int main() { Derive d; // 取出第一个虚函数表(Base1的表) VFPTR* vtable1 = (VFPTR*)(*(int*)&d); PrintVTable(vtable1); // 取出第二个虚函数表(Base2的表):Base1部分的大小为vptr(4字节) + _b1(4字节) = 8字节 VFPTR* vtable2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1))); PrintVTable(vtable2); return 0; }输出结果为:
2.2 多继承(有虚函数覆盖)的虚函数表
当派生类重写多个基类中的虚函数时,每个基类的虚函数表中对应的函数指针都会被替换。
代码语言:javascript
AI代码解释
class Base1 { public: virtual void func1() { cout << "Base1::func1()" << endl; } virtual void func2() { cout << "Base1::func2()" << endl; } }; class Base2 { public: virtual void func1() { cout << "Base2::func1()" << endl; } // 与Base1的func1同名 virtual void func3() { cout << "Base2::func3()" << endl; } }; class Derive : public Base1, public Base2 { public: // 同时覆盖Base1和Base2的func1 virtual void func1() { cout << "Derive::func1()" << endl; } };2.2.1 虚函数表的覆盖规则
Derive 的虚函数表结构如下:
- Base1 的虚函数表:
Derive::func1(覆盖)→Base1::func2 - Base2 的虚函数表:
Derive::func1(覆盖)→Base2::func3
这意味着,无论通过 Base1 指针还是 Base2 指针调用func1,最终都会执行Derive::func1:
www.dongchedi.com/article/7595302803858293310
www.dongchedi.com/article/7595302533728551486
www.dongchedi.com/article/7595303526705152574
www.dongchedi.com/article/7595302533728911934
www.dongchedi.com/article/7595302732916277822
www.dongchedi.com/article/7595300345145164313
www.dongchedi.com/article/7595287730565710361
www.dongchedi.com/article/7595287469617529368
www.dongchedi.com/article/7595289256520663576
www.dongchedi.com/article/7595287092394000920
www.dongchedi.com/article/7595286612611957273
www.dongchedi.com/article/7595285133738410521
www.dongchedi.com/article/7595285905729487384
www.dongchedi.com/article/7595287514580435481
www.dongchedi.com/article/7595285072006382105
www.dongchedi.com/article/7595285719334502936
www.dongchedi.com/article/7595285631619007000
www.dongchedi.com/article/7595277509785453081
www.dongchedi.com/article/7595276630432760345
www.dongchedi.com/article/7595275735409967640
www.dongchedi.com/article/7595277089067549246
www.dongchedi.com/article/7595276413155295769
www.dongchedi.com/article/7595276373905195544
www.dongchedi.com/article/7595274144955499033
www.dongchedi.com/article/7595274423667048984
www.dongchedi.com/article/7595275907217195545
www.dongchedi.com/article/7595274913787953689
www.dongchedi.com/article/7595274806057337368
www.dongchedi.com/article/7595274833727406617
www.dongchedi.com/article/7595255011878208062
www.dongchedi.com/article/7595255232834159166
www.dongchedi.com/article/7595244636982379033
www.dongchedi.com/article/7595246619336000062
www.dongchedi.com/article/7595245102663352894
www.dongchedi.com/article/7595246113737982526
www.dongchedi.com/article/7595238963515146814
www.dongchedi.com/article/7595238605032292888
www.dongchedi.com/article/7595237840809198105
www.dongchedi.com/article/7595237829975212569
www.dongchedi.com/article/7595237024668877336
www.dongchedi.com/article/7594914712018600510
www.dongchedi.com/article/7594913283375907352
www.dongchedi.com/article/7594914016129106456
www.dongchedi.com/article/7594914424213766718
www.dongchedi.com/article/7594913083894891033
www.dongchedi.com/article/7594913119710069310
www.dongchedi.com/article/7594912459706696254
www.dongchedi.com/article/7594911633613390360
www.dongchedi.com/article/7594909036307595800
www.dongchedi.com/article/7594910057444786750
www.dongchedi.com/article/7594909893274927641
www.dongchedi.com/article/7594909974816588350
www.dongchedi.com/article/7594909035217404441
www.dongchedi.com/article/7594908551181615678
www.dongchedi.com/article/7594906883010855486
www.dongchedi.com/article/7594907513641058878
www.dongchedi.com/article/7594905549272285720
www.dongchedi.com/article/7594906861996884505
www.dongchedi.com/article/7594906134906421785
www.dongchedi.com/article/7594905869373096472
www.dongchedi.com/article/7594904114086560281
www.dongchedi.com/article/7594903301414650392
www.dongchedi.com/article/7594901235942228542
www.dongchedi.com/article/7594901805579452953
www.dongchedi.com/article/7594900315275657752
www.dongchedi.com/article/7594899458794799641
www.dongchedi.com/article/7594900266315645465
www.dongchedi.com/article/7594900151853236798
www.dongchedi.com/article/7594898846111580697
www.dongchedi.com/article/7594899021756776984
www.dongchedi.com/article/7594897166766965272
www.dongchedi.com/article/7594897310665245246
www.dongchedi.com/article/7594897192310407705
www.dongchedi.com/article/7594897631617794585
www.dongchedi.com/article/7594896580080435737
www.dongchedi.com/article/7594895704896684568
www.dongchedi.com/article/7594897374330503705
www.dongchedi.com/article/7594895618720449048
www.dongchedi.com/article/7594896034183397912
www.dongchedi.com/article/7594896087656694334