面试题答案
一键面试-
编译器和运行时系统确保准确调用派生类函数的机制
- 编译器:
- 在编译阶段,编译器会为每个包含虚函数的类创建一个虚函数表(VTable)。虚函数表是一个存储类的虚函数地址的数组。
- 对于基类,虚函数表中存放的是基类虚函数的地址。当派生类重写了基类的虚函数时,派生类的虚函数表中对应的虚函数地址会被替换为派生类重写后的函数地址。如果派生类没有重写某个虚函数,那么在其虚函数表中,该虚函数地址仍指向基类的虚函数。
- 编译器还会为每个对象添加一个虚指针(VPtr),这个指针指向该对象所属类的虚函数表。在对象构造时,VPtr 会被正确初始化。
- 运行时系统:
- 当通过基类指针调用虚函数时,运行时系统首先通过该指针找到对象的 VPtr,然后根据 VPtr 找到对应的虚函数表。
- 从虚函数表中获取到正确的虚函数地址,然后调用该函数,这样就能确保调用到合适的派生类函数。
- 编译器:
-
菱形继承场景下多态实现的潜在问题
- 数据冗余问题:在菱形继承中,从基类继承下来的数据会在多个派生类路径中重复出现。例如,有基类
A
,派生类B
和C
都继承自A
,然后D
同时继承自B
和C
。这样D
中会有两份A
的数据成员,这不仅浪费空间,还可能导致访问数据成员时的歧义。 - 虚函数表混乱问题:由于数据冗余,虚函数表的管理也变得复杂。如果处理不当,可能会导致虚函数调用错误,比如在调用虚函数时,无法准确找到正确的函数地址。
- 数据冗余问题:在菱形继承中,从基类继承下来的数据会在多个派生类路径中重复出现。例如,有基类
-
解决菱形继承问题的方法
- 使用虚继承:
- 在 C++ 中,可以通过虚继承来解决菱形继承的问题。例如,定义
B
和C
继承自A
时使用虚继承:
- 在 C++ 中,可以通过虚继承来解决菱形继承的问题。例如,定义
- 使用虚继承:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
- 虚继承使得从不同路径继承过来的基类 `A` 的数据成员在派生类 `D` 中只存在一份,避免了数据冗余。同时,虚继承也对虚函数表进行了合理的调整,确保虚函数调用的正确性。在这种情况下,编译器会构建一个更复杂的虚函数表结构来处理多重继承和虚继承的情况,使得虚函数调用能够准确无误地找到合适的函数实现。