面试题答案
一键面试虚基类的作用
在这种多层继承体系中,虚基类A
的主要作用是避免在D
类对象中出现A
类成员的多份拷贝。如果不使用虚基类,由于B
和C
都继承自A
,D
继承自B
和C
,那么D
类对象中会包含两份A
类的成员,这会导致数据冗余和访问歧义。使用虚基类可以确保D
类对象中只有一份A
类的成员。
构造函数调用顺序
- 首先调用虚基类
A
的构造函数。无论虚基类在继承体系中处于多深的层次,它总是最先被构造。 - 然后按照继承列表中出现的顺序,依次调用直接基类的构造函数。在
D
类的情况中,先调用B
的构造函数,再调用C
的构造函数。 - 最后调用自身
D
的构造函数。
注意事项
- 构造函数参数传递:在各层类的构造函数中,需要确保虚基类构造函数所需的参数能够正确传递。通常是在最底层的派生类(如
D
)的构造函数初始化列表中直接调用虚基类A
的构造函数,这样可以保证虚基类构造函数在所有其他基类构造函数之前被调用。 - 避免重复初始化:由于虚基类只会被构造一次,在不同的直接基类(如
B
和C
)的构造函数中不需要再次调用虚基类的构造函数,否则会导致编译错误。
完整代码实现
#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;
}
在上述代码中:
A
是虚基类,B
和C
通过virtual public A
继承自A
,D
同时继承自B
和C
。- 在
D
的构造函数初始化列表中,显式调用了虚基类A
的构造函数,同时也调用了B
和C
的构造函数。虽然B
和C
的构造函数也会调用A
的构造函数,但由于A
是虚基类,只会被构造一次。 - 运行程序时,可以看到构造函数和析构函数的调用顺序符合前面所述的规则。