面试题答案
一键面试构造函数调用顺序
- 调用
A
的构造函数:因为A
是最顶层的基类,在创建D
对象时,会首先调用A
的构造函数,初始化从A
继承来的成员。 - 调用
B
的构造函数:B
继承自A
,在A
构造完成后,调用B
的构造函数,初始化B
新增的成员。 - 调用
C
的构造函数:C
继承自B
,在B
构造完成后,调用C
的构造函数,初始化C
新增的成员。 - 调用
E
的构造函数:由于D
多重继承自C
和E
,在C
的基类构造完成后,会调用E
的构造函数,初始化从E
继承来的成员。 - 调用
C
的构造函数(再次):这里是D
从C
继承部分的构造函数,初始化C
在D
中的特定成员。 - 调用
D
的构造函数:最后调用D
自身的构造函数,初始化D
自己新增的成员。
优化策略
- 使用初始化列表:
- 适用场景:适用于所有构造函数,特别是在构造函数参数众多或者成员变量初始化复杂的情况下。
- 潜在风险:如果初始化列表的顺序与成员变量声明顺序不一致,可能导致初始化顺序混乱,引起未定义行为。例如,成员变量
a
依赖于成员变量b
初始化后的值,但在初始化列表中先初始化a
后初始化b
。
- 减少不必要的继承层次:
- 适用场景:当发现某些中间层继承关系并没有带来实质性的功能扩展,仅仅是为了代码组织时,可以考虑合并或简化继承层次。
- 潜在风险:过度简化可能导致代码失去原有的清晰结构,增加代码理解和维护的难度。例如,原本清晰的功能划分因为继承层次的减少而变得模糊。
- 延迟初始化:
- 适用场景:对于一些资源开销较大的成员变量,在对象创建时并不马上需要使用,可以采用延迟初始化。例如,一个数据库连接对象,在对象创建初期可能不需要马上连接数据库。
- 潜在风险:如果在需要使用该成员变量时没有正确初始化,可能导致运行时错误。同时,增加了代码的复杂性,需要额外的逻辑来判断成员变量是否已经初始化。