虚函数指针和虚函数表的创建时机和存放位置
问题
虚函数指针和虚函数表的创建时机和存放位置
我的回答
虚函数表(vtable)是存放在程序的只读数据段(.rodata)中的。这是因为虚函数表是编译时就确定的,对于每个包含虚函数的类,编译器都会创建一个对应的虚函数表,其中存储了该类的虚函数地址。将它放在只读数据段可以防止程序意外修改这些函数指针,提高安全性。
而虚函数指针(vptr)是存放在对象的内存布局中的,通常位于对象的开头位置。当我们创建一个包含虚函数的类的对象时,这个对象会包含一个指向对应虚函数表的指针。由于对象可能在栈上创建,也可能在堆上创建,所以虚函数指针的具体存储区域取决于对象本身的分配位置:
- 如果对象是栈上的局部变量,那么vptr就在栈内存中
- 如果对象是通过new动态分配的,那么vptr就在堆内存中
- 如果对象是全局或静态的,那么vptr就在数据段中
关于创建时机:
虚函数表是在编译期就创建好的,当编译器编译一个包含虚函数的类时,就会为这个类生成一个虚函数表。
而虚函数指针是在对象构造时被初始化的,具体来说是在执行基类构造函数之前,编译器会自动插入代码来初始化vptr,让它指向正确的虚函数表。这个初始化发生在任何用户代码执行之前。
在继承场景中,情况会更复杂一些。当派生类对象构造时,vptr最初会指向基类的虚函数表,然后在执行派生类构造函数时,vptr会被更新为指向派生类的虚函数表。这就是为什么在基类构造函数中调用虚函数时,会调用到基类的实现而不是派生类的实现,因为此时vptr还指向基类的虚函数表。