MST

星途 面试题库

面试题:C++虚基类在复杂继承体系中的应用

假设有一个多层继承体系,顶层是虚基类A,中间层有B和C继承自A,底层有D同时继承自B和C。请详细说明在这种情况下虚基类的作用,以及在各层类的构造函数中对虚基类构造函数的调用顺序和注意事项,并编写完整代码实现该继承体系。
28.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚基类的作用

在这种多层继承体系中,虚基类A的主要作用是避免在D类对象中出现A类成员的多份拷贝。如果不使用虚基类,由于BC都继承自AD继承自BC,那么D类对象中会包含两份A类的成员,这会导致数据冗余和访问歧义。使用虚基类可以确保D类对象中只有一份A类的成员。

构造函数调用顺序

  1. 首先调用虚基类A的构造函数。无论虚基类在继承体系中处于多深的层次,它总是最先被构造。
  2. 然后按照继承列表中出现的顺序,依次调用直接基类的构造函数。在D类的情况中,先调用B的构造函数,再调用C的构造函数。
  3. 最后调用自身D的构造函数。

注意事项

  1. 构造函数参数传递:在各层类的构造函数中,需要确保虚基类构造函数所需的参数能够正确传递。通常是在最底层的派生类(如D)的构造函数初始化列表中直接调用虚基类A的构造函数,这样可以保证虚基类构造函数在所有其他基类构造函数之前被调用。
  2. 避免重复初始化:由于虚基类只会被构造一次,在不同的直接基类(如BC)的构造函数中不需要再次调用虚基类的构造函数,否则会导致编译错误。

完整代码实现

#include <iostream>

// 虚基类A
class A {
public:
    A(int value) : data(value) {
        std::cout << "A constructor: " << data << std::endl;
    }
    ~A() {
        std::cout << "A destructor: " << data << std::endl;
    }
protected:
    int data;
};

// 中间层类B继承自虚基类A
class B : virtual public A {
public:
    B(int value) : A(value) {
        std::cout << "B constructor" << std::endl;
    }
    ~B() {
        std::cout << "B destructor" << std::endl;
    }
};

// 中间层类C继承自虚基类A
class C : virtual public A {
public:
    C(int value) : A(value) {
        std::cout << "C constructor" << std::endl;
    }
    ~C() {
        std::cout << "C destructor" << std::endl;
    }
};

// 底层类D同时继承自B和C
class D : public B, public C {
public:
    D(int value) : A(value), B(value), C(value) {
        std::cout << "D constructor" << std::endl;
    }
    ~D() {
        std::cout << "D destructor" << std::endl;
    }
};

int main() {
    D d(10);
    return 0;
}

在上述代码中:

  1. A是虚基类,BC通过virtual public A继承自AD同时继承自BC
  2. D的构造函数初始化列表中,显式调用了虚基类A的构造函数,同时也调用了BC的构造函数。虽然BC的构造函数也会调用A的构造函数,但由于A是虚基类,只会被构造一次。
  3. 运行程序时,可以看到构造函数和析构函数的调用顺序符合前面所述的规则。