面试题答案
一键面试1. 缺省构造函数在对象创建过程中的作用
- 普通多重继承:在普通多重继承体系中,当创建派生类对象时,基类的缺省构造函数会按照继承列表的顺序依次被调用。派生类对象会包含各个基类子对象,每个基类子对象通过其缺省构造函数进行初始化。例如:
class Base1 {
public:
Base1() { std::cout << "Base1 constructor" << std::endl; }
};
class Base2 {
public:
Base2() { std::cout << "Base2 constructor" << std::endl; }
};
class Derived : public Base1, public Base2 {
public:
Derived() { std::cout << "Derived constructor" << std::endl; }
};
当创建Derived d;
时,先调用Base1
的构造函数,再调用Base2
的构造函数,最后调用Derived
的构造函数。
- 虚继承:在虚继承体系中,虚基类的缺省构造函数会在最底层派生类的构造函数中被调用,且只会被调用一次。这是因为虚基类子对象由最底层派生类负责初始化,以确保在多重继承路径中不会出现重复的虚基类子对象。例如:
class VirtualBase {
public:
VirtualBase() { std::cout << "VirtualBase constructor" << std::endl; }
};
class Derived1 : virtual public VirtualBase {
public:
Derived1() { std::cout << "Derived1 constructor" << std::endl; }
};
class Derived2 : virtual public VirtualBase {
public:
Derived2() { std::cout << "Derived2 constructor" << std::endl; }
};
class FinalDerived : public Derived1, public Derived2 {
public:
FinalDerived() { std::cout << "FinalDerived constructor" << std::endl; }
};
当创建FinalDerived fd;
时,先调用VirtualBase
的构造函数,然后按照继承顺序依次调用Derived1
和Derived2
的构造函数,最后调用FinalDerived
的构造函数。
2. 析构函数在对象销毁过程中的作用
- 普通多重继承:当派生类对象被销毁时,析构函数的调用顺序与构造函数相反。首先调用派生类的析构函数,然后按照继承列表的逆序依次调用各个基类的析构函数。例如,对于前面的
Derived
类,当Derived
对象被销毁时,先调用Derived
的析构函数,再调用Base2
的析构函数,最后调用Base1
的析构函数。 - 虚继承:在虚继承体系中,析构函数的调用顺序同样与构造函数相反。最底层派生类的析构函数先被调用,然后按照继承顺序依次调用各个直接基类的析构函数,其中虚基类的析构函数在其直接派生类的析构函数中被调用。例如,对于
FinalDerived
类,当FinalDerived
对象被销毁时,先调用FinalDerived
的析构函数,然后依次调用Derived2
、Derived1
的析构函数,最后调用VirtualBase
的析构函数。
3. 拷贝构造函数在对象复制过程中的作用
- 普通多重继承:当进行对象复制(例如通过
Derived d2(d1);
)时,派生类的拷贝构造函数会调用基类的拷贝构造函数来复制基类子对象。如果派生类没有定义拷贝构造函数,编译器会生成一个缺省的拷贝构造函数,它会按成员逐一复制,包括基类子对象。 - 虚继承:在虚继承体系中,拷贝构造函数同样需要处理虚基类子对象的复制。最底层派生类的拷贝构造函数负责调用虚基类的拷贝构造函数来复制虚基类子对象,同时也需要调用直接基类的拷贝构造函数来复制其他基类子对象。如果没有正确实现拷贝构造函数,可能会导致虚基类子对象复制错误,例如重复复制或未正确初始化。
4. 虚继承情况下的特殊处理机制
- 虚基类表:编译器会为每个包含虚继承的类生成一个虚基类表,用于记录虚基类子对象的偏移量等信息。这使得在多重继承路径中能够正确定位和访问唯一的虚基类子对象。
- 初始化责任:如前所述,最底层派生类负责初始化虚基类子对象,这确保了虚基类子对象在整个继承体系中的唯一性。
5. 可能存在的陷阱
- 忘记初始化虚基类:如果最底层派生类的构造函数没有显式调用虚基类的构造函数,编译器可能无法正确初始化虚基类子对象,导致未定义行为。
- 拷贝构造函数和赋值运算符重载问题:在虚继承体系中,如果没有正确实现拷贝构造函数和赋值运算符重载,可能会导致虚基类子对象的复制错误,例如重复复制或内存泄漏。
- 对象切割:在涉及虚继承的多重继承体系中,进行对象切割(将派生类对象赋值给基类对象)时,可能会丢失虚基类子对象的部分信息,因为基类对象可能无法正确处理虚基类的特殊布局。