面试题答案
一键面试性能差异
- 初始化列表:
- 直接初始化成员变量,对于类类型成员变量,会调用其对应类型的构造函数进行初始化。这种方式效率较高,特别是对于那些没有默认构造函数,或者在默认构造函数之后还需要进行额外赋值操作的成员变量。
- 因为成员变量在进入构造函数体之前就已经被初始化完成,减少了构造函数体内可能的额外赋值开销。
- 构造函数体内赋值:
- 先调用成员变量的默认构造函数(如果存在),然后在构造函数体内再进行赋值操作。这意味着对于每个成员变量,可能会有一次默认构造和一次赋值操作,相比初始化列表会有更多的开销。
显著提升性能的情况
- 成员变量为类类型且没有默认构造函数:
- 如果类类型成员变量没有默认构造函数,必须使用初始化列表来初始化,否则无法通过编译。例如:
class NoDefaultConstructor {
public:
NoDefaultConstructor(int value) : data(value) {}
private:
int data;
};
class MyClass {
public:
// 必须使用初始化列表初始化noDefault
MyClass(int value) : noDefault(value) {}
private:
NoDefaultConstructor noDefault;
};
- 如果尝试在构造函数体内赋值,如
MyClass(int value) { noDefault = NoDefaultConstructor(value); }
,编译器会报错,因为noDefault
在进入构造函数体前需要先默认构造,而该类型没有默认构造函数。
- 成员变量为const类型或引用类型:
- const类型和引用类型的成员变量必须在定义时初始化,所以只能在初始化列表中进行初始化。例如:
class ConstAndRefClass {
public:
ConstAndRefClass(int& refValue) : ref(refValue), constant(10) {}
private:
int& ref;
const int constant;
};
- 频繁构造和析构的场景:
- 当对象频繁构造和析构时,使用初始化列表减少不必要的默认构造和赋值操作能显著提升性能。例如,在一个循环中构造大量对象:
#include <iostream>
class Example {
public:
Example(int value) : data(value) {}
private:
int data;
};
int main() {
for (int i = 0; i < 1000000; ++i) {
Example ex(i);
}
return 0;
}
- 如果使用构造函数体内赋值,每次构造对象时会多一次默认构造和一次赋值操作,在大量构造的场景下,性能损耗会比较明显。
综上所述,在上述几种情况下,使用初始化列表能显著提升性能。在一般情况下,即使成员变量有默认构造函数,使用初始化列表也可能带来轻微的性能提升,并且代码逻辑更清晰,表明成员变量是如何初始化的。