MST
星途 面试题库

面试题:C++存储位置对虚函数表及对象布局的影响

从C++变量存储位置的角度出发,阐述虚函数表的存储位置与类对象布局之间的关系。当类中有不同存储位置的成员变量时,如何影响虚函数表指针的位置以及对象在内存中的布局?并分析这种布局对运行时多态实现的底层原理。
12.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚函数表的存储位置与类对象布局的关系

  1. 虚函数表存储位置:在C++中,虚函数表(vtable)存储在程序的只读数据段(通常称为.rodata段),这是因为虚函数表内容在程序运行期间不会改变,将其放在只读数据段可防止意外修改。
  2. 类对象布局与虚函数表指针:对于包含虚函数的类,其对象布局的第一个成员是虚函数表指针(vptr)。这个指针指向该类对应的虚函数表。例如,假设有类Base
class Base {
public:
    virtual void func() {}
};

Base类对象在内存中的布局首先是vptr,然后才是其他成员变量。这种布局使得在运行时能够快速定位虚函数表,进而找到要调用的虚函数。

不同存储位置成员变量对虚函数表指针位置及对象内存布局的影响

  1. 静态成员变量:静态成员变量不属于类对象的一部分,它们存储在全局数据区,与类对象的内存布局无关。因此,静态成员变量不会影响虚函数表指针的位置和类对象在内存中的布局。例如:
class Example {
public:
    static int s_var;
    virtual void func() {}
};

这里Example类对象布局依然是vptr在前,不受static int s_var的影响。 2. 普通成员变量:普通成员变量按照声明顺序依次存储在虚函数表指针之后。例如:

class AnotherExample {
public:
    virtual void func() {}
    int a;
    double b;
};

AnotherExample类对象在内存中的布局是vptrab。这种布局保证了对象内存布局的一致性和可预测性,编译器能够根据偏移量准确访问各个成员变量。

这种布局对运行时多态实现的底层原理

  1. 动态绑定基础:运行时多态通过虚函数和虚函数表实现。当使用基类指针或引用调用虚函数时,程序运行时会根据对象实际类型(而非指针或引用类型)来确定调用的具体函数。由于对象布局中vptr位于首位,通过对象地址即可快速获取虚函数表指针,进而定位到虚函数表。
  2. 具体过程:假设存在继承关系Base(基类)和Derived(派生类),Derived重写了Base的虚函数。当有Base* ptr = new Derived();ptr->func();调用时:
    • 首先,根据ptr指向的对象地址,找到对象布局中的vptr
    • 然后,通过vptr找到Derived类的虚函数表。
    • 最后,在虚函数表中找到被重写的func函数的地址并调用。 这种基于对象布局和虚函数表的机制,使得C++能够在运行时动态地确定要调用的虚函数,实现运行时多态。