面试题答案
一键面试可能导致性能损耗的常见因素
- 复杂初始化:构造函数中进行复杂计算、大量内存分配等操作,如在构造函数中初始化一个庞大的数组或进行复杂的数学运算。例如:
class Example {
private:
int* largeArray;
public:
Example() {
largeArray = new int[1000000];
for (int i = 0; i < 1000000; i++) {
largeArray[i] = i * i;
}
}
~Example() {
delete[] largeArray;
}
};
这里构造函数初始化庞大数组并计算每个元素值,析构函数释放内存,这两个操作都可能带来性能损耗。
- 不必要的动态分配:频繁在构造函数中动态分配内存,且析构函数释放,如每次构造对象都创建新的字符串对象。
class StringHolder {
private:
char* str;
public:
StringHolder(const char* s) {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
~StringHolder() {
delete[] str;
}
};
- 基类和成员初始化开销:如果基类构造函数或成员对象构造函数本身复杂,会导致派生类对象构造时开销增大。例如:
class Base {
public:
Base() {
// 复杂初始化操作
for (int i = 0; i < 10000; i++) {
// 一些复杂计算
}
}
};
class Derived : public Base {
private:
int member;
public:
Derived() : member(0) {
// 本身初始化简单,但基类复杂
}
};
优化方式
- 减少复杂计算:将复杂计算推迟到真正需要时进行,或者在构造函数外进行预计算。对于前面的
Example
类,可以这样优化:
class Example {
private:
int* largeArray;
public:
Example() {
largeArray = new int[1000000];
}
void calculateArray() {
for (int i = 0; i < 1000000; i++) {
largeArray[i] = i * i;
}
}
~Example() {
delete[] largeArray;
}
};
使用时,先创建对象,在合适时机调用 calculateArray
方法。
- 避免不必要的动态分配:尽量使用栈上对象或智能指针管理动态分配。对于
StringHolder
类,可使用std::string
:
class StringHolder {
private:
std::string str;
public:
StringHolder(const char* s) : str(s) {
// std::string 内部管理内存,简化构造和析构
}
// 无需显式析构函数,std::string 自动管理内存
};
- 优化基类和成员初始化:确保基类和成员对象的构造函数尽可能高效。如果可能,对基类和成员对象进行惰性初始化。例如,对于
Derived
类,如果Base
类中复杂操作不是必须在构造时完成,可以将其放到一个方法中,在需要时调用。
class Base {
private:
bool initialized = false;
public:
void performComplexInit() {
if (!initialized) {
for (int i = 0; i < 10000; i++) {
// 一些复杂计算
}
initialized = true;
}
}
};
class Derived : public Base {
private:
int member;
public:
Derived() : member(0) {
// 构造时不进行复杂操作
}
void useBaseFunction() {
performComplexInit();
// 使用基类功能
}
};