版权声明:本文为CSDN博主「Runner_of_nku」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Runner_of_nku/article/details/115251821
一图以镇之
c/c++程序所占用的内存一共分为五种:
栈区,堆区,程序代码区,全局数据区(静态区),文字常量区.
显而易见,虚函数表存放在全局数据区.
下面开始试验
class A{
public:
virtual void a() {};
};
class B{
public:
virtual int b(){return 3;}
};
class C : public A,B{
public:
virtual int c(){return 4;}
};
首先定义三个类,C继承A和B,那么根据经验,C中会有一个指向A和B的虚表指针,如此我们分别打印sizeof(...)可以得到8 8 16的结果
那么C中自己也是有一个自己的虚函数的,C中的虚函数指针放哪里了呢,答案就是C中的虚函数指针放到了指向A虚表的指针内容的后面
也就是说C中两个虚表指针*a *b
*a中存放的是A中虚函数指针和C的虚函数指针
*b存放的是b的虚函数指针
A a;B b;C c;
// *(int64_t*)取值操作
// &a就是a的地址
// *(int64_t*)&a 取a实例内存里的值,即a的虚表指针
// *(int64_t*)(*(int64_t*)&a) 取a的虚表指针指向的值,即虚函数指针
cout<<(*(int64_t*)(*(int64_t*)(&a)))<
上面的结果为打印虚函数指针,由于c中有两个指针,第二个指针指向b的表,所以下面两行输出相同。如果没有+1,则和第一行第三行输出相同。
感觉这种题没什么必要,内卷用。气
这是属于编译器编译阶段发生的事情。
实例化一个类(class)会调用构造函数,const成员声明必须要初始化,因此只能在初始化列表去初始化const成员。虚函数表会随着构造函数一起被建立,其地址会赋值给虚函数表指针(vptr),指向虚函数表,和普通指针一样大小4字节(64位电脑8字节),并且在类的开始位置。
1.虚函数表是全局共享的元素,即全局仅有一个.
2.虚函数表类似一个数组,类对象中存储vptr指针,指向虚函数表.即虚函数表不是函数,不是程序代码,不肯能存储在代码段.
3.虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不再堆中.
根据以上特征,虚函数表类似于类中静态成员变量.静态成员变量也是全局共享,大小确定.
几个值得注意的问题
虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别。