面试题答案
一键面试1. C++虚函数表的基本结构
- 虚函数表是一个由函数指针组成的数组。当一个类中声明了虚函数时,编译器会为该类生成一个虚函数表。
- 表中的每一个元素都是一个指向虚函数的指针。虚函数表的指针(vptr)会被编译器悄悄地插入到类的对象中,通常位于对象内存布局的开始位置。
- 对于派生类,如果它没有重写基类的虚函数,虚函数表中相应位置的指针会指向基类的虚函数实现;如果派生类重写了基类的虚函数,虚函数表中对应位置的指针会指向派生类自己的虚函数实现。
2. 通过对象指针访问虚函数表及原理
假设类A
包含虚函数,类B
继承自类A
:
class A {
public:
virtual void func1() {}
virtual void func2() {}
};
class B : public A {
public:
void func1() override {}
};
- 访问虚函数表:
- 在C++中,可以通过对象指针访问虚函数表。由于虚函数表指针(vptr)通常位于对象内存布局的开始位置,所以可以通过以下方式获取虚函数表指针(这里通过
reinterpret_cast
进行指针类型转换,这种方式比较底层且不安全,实际应用中不建议直接这样操作,但能说明原理):
- 在C++中,可以通过对象指针访问虚函数表。由于虚函数表指针(vptr)通常位于对象内存布局的开始位置,所以可以通过以下方式获取虚函数表指针(这里通过
A* aPtr = new B();
typedef void (*FuncPtr)();
FuncPtr* vtable = reinterpret_cast<FuncPtr*>(*(reinterpret_cast<size_t*>(aPtr)));
- 上述代码中,首先通过
reinterpret_cast<size_t*>(aPtr)
将aPtr
转换为size_t*
类型,这样就可以获取对象开始位置的值(即vptr),然后通过*(reinterpret_cast<size_t*>(aPtr))
获取vptr的值,再将这个值转换为FuncPtr*
类型,即虚函数表指针。 - 原理:
- 动态绑定:当通过对象指针调用虚函数时,实际调用的函数是在运行时根据对象的实际类型确定的。这是因为对象中包含虚函数表指针,通过这个指针可以找到对应的虚函数表。
- 虚函数表查找:在运行时,程序根据对象指针找到对象内存布局中的虚函数表指针(vptr),然后通过虚函数表指针找到虚函数表。虚函数表中每个函数指针的位置是固定的,根据虚函数在类中声明的顺序确定。例如,第一个声明的虚函数对应的函数指针在虚函数表的第一个位置,第二个声明的虚函数对应的函数指针在虚函数表的第二个位置,以此类推。所以,根据虚函数在表中的位置,可以找到要调用的虚函数的实际地址并进行调用。在上述例子中,
B
类重写了func1
,所以B
类对象的虚函数表中func1
位置的指针指向B::func1
,而func2
位置的指针指向A::func2
(因为B
类没有重写func2
)。当通过aPtr
(实际指向B
类对象)调用func1
时,就会根据虚函数表找到B::func1
并调用。