MST

星途 面试题库

面试题:C++虚函数表与内存布局关系

在C++中,含有虚函数的类的对象内存布局是怎样的?虚函数表在内存中的位置以及与对象内存布局的关系是怎样的?请详细说明,并举例代码展示如何通过指针访问虚函数表及虚函数。
42.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 含有虚函数的类的对象内存布局
    • 当一个类含有虚函数时,对象的内存布局通常首先包含一个虚函数表指针(vptr)。这个指针指向该类的虚函数表(vtable)。
    • 之后紧跟类的其他成员变量。如果类有父类,且父类也有虚函数,父类部分的内存布局同样遵循此规则,先有父类的虚函数表指针(如果父类有虚函数),然后是父类的成员变量,接着才是子类新增的成员变量。
  2. 虚函数表在内存中的位置
    • 虚函数表存储在程序的只读数据段(一般也称为常量区)。它是一个全局的数据结构,属于类,而不是类的对象。每个包含虚函数的类都有自己的虚函数表。
  3. 虚函数表与对象内存布局的关系
    • 对象中的虚函数表指针(vptr)指向对应的虚函数表。当通过对象指针或引用调用虚函数时,实际是通过虚函数表指针找到虚函数表,再从虚函数表中获取对应虚函数的地址并调用。
  4. 示例代码
#include <iostream>

class Base {
public:
    virtual void virtualFunction() {
        std::cout << "Base::virtualFunction" << std::endl;
    }
};

class Derived : public Base {
public:
    void virtualFunction() override {
        std::cout << "Derived::virtualFunction" << std::endl;
    }
};

// 辅助函数,用于获取虚函数表指针
typedef void (*Func)();
void** getVTablePtr(void* obj) {
    return *(void***)(obj);
}

// 辅助函数,用于获取虚函数表中特定虚函数的地址
Func getVirtualFunctionAddr(void** vTable, int index) {
    return (Func)(vTable[index]);
}

int main() {
    Base* basePtr = new Derived();

    // 获取对象的虚函数表指针
    void** vTablePtr = getVTablePtr(basePtr);

    // 获取虚函数表中第一个虚函数的地址(这里只有一个虚函数)
    Func funcAddr = getVirtualFunctionAddr(vTablePtr, 0);

    // 通过函数指针调用虚函数
    funcAddr();

    delete basePtr;
    return 0;
}

在上述代码中:

  • Base类是一个含有虚函数的基类,Derived类继承自Base类并重写了虚函数。
  • getVTablePtr函数用于获取对象的虚函数表指针。
  • getVirtualFunctionAddr函数用于从虚函数表中获取特定虚函数的地址。
  • main函数中,创建了一个Derived对象并通过指针访问其虚函数表及虚函数。注意,这种通过指针直接操作虚函数表的方式在实际开发中很少使用,主要用于理解原理。实际开发中应使用正常的虚函数调用机制。