面试题答案
一键面试优化性能的成员初始化策略
- 成员初始化列表:
- 对于类A中的自定义复杂类型成员变量,应使用成员初始化列表进行初始化。例如,如果类A有一个大数组成员
largeArray
和一个大型自定义类对象成员largeCustomObject
:
class CustomLargeClass { // 自定义大型类的定义 }; class A { int simpleVariable; int largeArray[10000]; CustomLargeClass largeCustomObject; public: A(int val) : simpleVariable(val), largeCustomObject(), largeArray() { // 构造函数体,这里对简单变量可以进行额外操作,但复杂类型已在初始化列表初始化 } };
- 这样做可以避免先默认构造复杂类型成员,然后再在构造函数体中赋值的额外开销。直接在初始化列表中初始化复杂类型成员,是直接调用其相应的构造函数,效率更高。
- 对于类A中的自定义复杂类型成员变量,应使用成员初始化列表进行初始化。例如,如果类A有一个大数组成员
- 按声明顺序初始化:成员初始化列表中的初始化顺序应与成员变量在类中声明的顺序一致。因为C++ 标准规定成员变量是按照声明顺序进行初始化的,即使在初始化列表中改变顺序,实际初始化顺序还是按声明顺序。例如:
正确的方式是:class A { int a; int b; public: A() : b(10), a(b) { // 这里a实际先初始化(按声明顺序),但b还未初始化,a的值是未定义的 } };
class A { int a; int b; public: A() : a(5), b(10) { // 按声明顺序初始化,a先初始化为5,b初始化为10 } };
不同初始化顺序可能带来的性能影响
- 复杂类型成员先初始化:如果先初始化简单类型成员,后初始化复杂类型成员,那么在复杂类型成员初始化过程中,若发生异常,简单类型成员已经初始化,可能需要额外的清理工作(如果简单类型成员初始化过程中分配了资源等情况)。而先初始化复杂类型成员可以减少这种潜在的额外清理开销。例如,假设复杂类型成员初始化失败概率较高,先初始化它可以避免不必要的简单类型成员初始化。
- 循环依赖问题:如果成员变量之间存在依赖关系,不按正确顺序初始化可能导致未定义行为或逻辑错误。例如,一个成员变量需要另一个成员变量的值来初始化,如果初始化顺序错误,可能使用到未初始化的值,这不仅会导致程序逻辑错误,也可能引发性能问题(如频繁的错误处理和调试开销)。如前面提到的
a
和b
的错误初始化顺序示例,会导致a
的值未定义,程序行为异常。如果在一个性能敏感的循环中依赖a
的正确值,就会导致性能问题。