面试题答案
一键面试多态的实现过程分析
在C++中,当存在多重继承且基类有虚函数时,多态的实现依赖于虚函数表(vtable)和虚函数表指针(vptr)。
- 虚函数表(vtable):每个包含虚函数的类都有一个虚函数表。虚函数表是一个数组,其中每个元素是一个指向虚函数的指针。在编译时,编译器会为每个包含虚函数的类创建一个虚函数表,并将虚函数的地址填充到该表中。
- 虚函数表指针(vptr):每个对象都有一个虚函数表指针,它指向该对象所属类的虚函数表。当对象调用虚函数时,实际上是通过虚函数表指针找到对应的虚函数表,然后根据虚函数在表中的索引来调用实际的函数。
在多重继承的情况下,类C从类A和类B继承,因此它会有两个虚函数表指针,分别指向A和B的虚函数表。当通过C的对象调用虚函数时,会根据对象的类型和虚函数的声明,找到对应的虚函数表,并调用相应的函数。
可能遇到的问题
- 菱形继承问题:如果A和B又继承自同一个基类,就会出现菱形继承,导致数据冗余和二义性问题。例如,假设A和B都继承自类D,那么C中会包含两份D的成员变量,这不仅浪费空间,而且在访问D的成员时会出现二义性。
- 虚函数表指针的管理:由于有多个虚函数表指针,对象的内存布局会变得复杂,增加了编译器和运行时系统的负担。特别是在对象的创建、销毁和赋值操作中,需要正确管理这些指针。
- 二义性调用:如果A和B中的虚函数具有相同的签名,那么在C中调用该虚函数时会出现二义性,编译器不知道应该调用A还是B的虚函数。
代码验证
#include <iostream>
class A {
public:
virtual void func() {
std::cout << "A::func()" << std::endl;
}
};
class B {
public:
virtual void func() {
std::cout << "B::func()" << std::endl;
}
};
class C : public A, public B {
public:
void func() override {
std::cout << "C::func()" << std::endl;
}
};
int main() {
A* a = new C();
a->func(); // 调用C::func()
B* b = new C();
b->func(); // 调用C::func()
C* c = new C();
c->func(); // 调用C::func()
delete a;
delete b;
delete c;
return 0;
}
上述代码中,类C继承自A和B,并且重写了它们的虚函数func。通过分别使用A*、B和C指针调用func,验证了多态的正确性。同时,如果在A和B中定义相同签名的虚函数,编译器会提示二义性错误,验证了可能遇到的问题。