MST

星途 面试题库

面试题:C++ 多态必要条件验证之高级难度

考虑多重继承和多态结合的场景,假设有类A、B、C,C继承自A和B,且A、B都有虚函数。请分析在这种情况下多态的实现过程以及可能遇到的问题,同时编写代码验证你的分析。
23.4万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

多态的实现过程分析

在C++中,当存在多重继承且基类有虚函数时,多态的实现依赖于虚函数表(vtable)和虚函数表指针(vptr)。

  1. 虚函数表(vtable):每个包含虚函数的类都有一个虚函数表。虚函数表是一个数组,其中每个元素是一个指向虚函数的指针。在编译时,编译器会为每个包含虚函数的类创建一个虚函数表,并将虚函数的地址填充到该表中。
  2. 虚函数表指针(vptr):每个对象都有一个虚函数表指针,它指向该对象所属类的虚函数表。当对象调用虚函数时,实际上是通过虚函数表指针找到对应的虚函数表,然后根据虚函数在表中的索引来调用实际的函数。

在多重继承的情况下,类C从类A和类B继承,因此它会有两个虚函数表指针,分别指向A和B的虚函数表。当通过C的对象调用虚函数时,会根据对象的类型和虚函数的声明,找到对应的虚函数表,并调用相应的函数。

可能遇到的问题

  1. 菱形继承问题:如果A和B又继承自同一个基类,就会出现菱形继承,导致数据冗余和二义性问题。例如,假设A和B都继承自类D,那么C中会包含两份D的成员变量,这不仅浪费空间,而且在访问D的成员时会出现二义性。
  2. 虚函数表指针的管理:由于有多个虚函数表指针,对象的内存布局会变得复杂,增加了编译器和运行时系统的负担。特别是在对象的创建、销毁和赋值操作中,需要正确管理这些指针。
  3. 二义性调用:如果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中定义相同签名的虚函数,编译器会提示二义性错误,验证了可能遇到的问题。