继承体系下虚函数表的初始化

有时会被问到这样一个问题,构造函数和析构函数里面可以调用虚函数吗?或许我们知道最好不要那样做,但是为什么呢?写个例子测试一下就知道这并没什么问题,程序也许可能按预期正常执行,但是语法上通过的东西,未必真的就make sense。《深度探索C++对象模型》一书中分析了对象的创建过程,当然也包括虚函数表的部分。首先看原文,关于编译器对构造函数的扩充:


    1.记录在member initialization list中的data members初始化操作会被放进constructor的函数本身,并以members的声明顺序为顺序.
    2.如果有一个member并没有出现在member initialization list中,但它有一个default constructor,那么该default constructor必须被调用.
    3.在那之前,如果 class object有 virtual table pointers,它们必须被设定初值,指向适当的 virtual tables.
    4.在那之前,所有上一层的base class constructors必须被调用,以base class 的声明顺序为顺序(与member initialization list中的顺序没关联):

        如果base class 被列于member initialization list中,那么任何明确指定的参数都应该传递过去.
        如果base class 没有被列于member initialization list中,而它有default constructor(或default memberwise copy constructor),那么就调用它.
        如果base class 是多重继承下的第二或后继的base class,那么 this 指针必须有所调整
### 关于 C++ 虚函数表存储虚函数地址的原理 #### 什么是虚函数表虚函数表(vtable)是一个由编译器自动生成的数据结构,通常表现为一个数组或指针列表。它的主要作用是在运行时支持动态绑定(即多态性),使得基类指针可以指向派生类对象并调用派生类中的重写方法。 在 C++ 中,当定义了一个含有虚函数的类时,编译器会为该类及其所有派生类创建一个对应的虚函数表[^1]。这个表中包含了类中所有虚函数的函数指针。 --- #### 虚函数表如何存储虚函数地址? 1. **虚函数表的内容** 对于每一个包含虚函数的类,编译器都会为其生成一个虚函数表。如果某个类是从另一个类继承而来的,则其虚函数表不仅包含自己声明的虚函数,还可能包含从父类继承下来的虚函数。这些虚函数按照一定的顺序排列在虚函数表中,并且每个虚函数都对应一个函数指针[^3]。 2. **虚函数表指针的位置** 编译器会在每个具有虚函数的对象实例的第一个字节位置放置一个隐式的指针 `_vptr`,称为虚函数表指针。此指针指向该类型的虚函数表。这种设计确保了访问虚函数表的速度最快,因为只需要通过对象首地址即可获取到虚函数表的入口[^2]。 3. **虚函数地址的具体存储方式** 当一个类被实例化成对象时,其实例内部会自动初始化 `_vptr` 指向正确的虚函数表。对于不同的具体类型(比如基类和派生类),它们各自的 `_vptr` 将分别指向各自独立的虚函数表。因此,在运行期间,即使使用的是基类指针或者引用,也可以通过查找实际对象所关联的虚函数表来定位最终要执行的方法版本。 4. **示意图解** 下面展示了一种典型的内存布局情况: ```plaintext +-------------------+ | _vptr (to vtable) | +-------------------+ | data member 1 | +-------------------+ | data member 2 | +-------------------+ Virtual Table: +----------+-----------+ | Index 0 | FuncPtr A*| +----------+-----------+ | Index 1 | FuncPtr B*| +----------+-----------+ ``` 这里 `FuncPtr A*`, `FuncPtr B*` 是具体的函数实现地址。假设有一个基类 `Base` 和派生类 `Derived` 都实现了相同的虚函数接口,那么两者会有自己的专属虚拟表格;尽管如此,只要经由同一份基类指针操作,仍能依据当前实体所属类别找到相应的行为逻辑。 5. **代码演示** 以下是简单的例子说明上述概念的应用场景: ```cpp #include <iostream> class Base { public: virtual void show() { std::cout << "Base\n"; } }; class Derived : public Base { public: void show() override { std::cout << "Derived\n"; } }; int main(){ Base *b = new Derived(); b->show(); // 输出 "Derived" delete b; } ``` 在这个案例里面,虽然变量 `b` 类型上属于 `Base*` ,但由于它实际上指向的是 `Derived` 实体,所以调用了后者重新定义过的 `show()` 方法。这背后就是依靠虚函数表完成的选择过程。 --- ### 总结 综上所述,C++虚函数表是一种高效的机制用来管理复杂继承体系下的行为多样性问题。通过对每种类别维护单独的一套映射关系——即将各个抽象出来的可变动作链接至确切的操作指令集——从而达成灵活又快速的目标导向控制流切换效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值