C++面试 虚函数表 虚函数指 内存位置

版权声明:本文为CSDN博主「Runner_of_nku」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Runner_of_nku/article/details/115251821


一图以镇之 

C++面试 虚函数表 虚函数指 内存位置_第1张图片

 

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.虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不再堆中.

根据以上特征,虚函数表类似于类中静态成员变量.静态成员变量也是全局共享,大小确定.

 

几个值得注意的问题

  1.   虚函数表是class specific的,也就是针对一个类来说的,这里有点像一个类里面的staic成员变量,即它是属于一个类所有对象的,不是属于某一个对象特有的,是一个类所有对象共有的。
  2.  虚函数表是编译器来选择实现的,编译器的种类不同,可能实现方式不一样,就像前面我们说的vptr在一个对象的最前面,但是也有其他实现方式,不过目前gcc 和微软的编译器都是将vptr放在对象内存布局的最前面。
  3.  虽然我们知道vptr指向虚函数表,那么虚函数表具体存放在内存哪个位置呢,虽然这里我们已经可以得到虚函数表的地址。实际上虚函数指针是在构造函数执行时初始化的,而虚函数表是存放在可执行文件中的。下面的一篇博客测试了微软的编译器将虚函数表存放在了目标文件或者可执行文件的常量段中,http://blog.csdn.net/vicness/article/details/3962767,不过我在gcc下的汇编文件中没有找到vtbl的具体存放位置,主要是对可执行文件的装载和运行原理还没有深刻的理解,相信不久有了这些知识之后会很轻松的找到虚函数表到底存放在目标文件的哪一个段中。
  4. 经过测试,在gcc编译器的实现中虚函数表vtable存放在可执行文件的只读数据段.rodata中。

虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别。

 

你可能感兴趣的:(面试系列,C++,c++,面试,虚函数)