面试题答案
一键面试函数设计
#include <iostream>
class BigData {
public:
BigData() {
std::cout << "BigData constructor" << std::endl;
}
~BigData() {
std::cout << "BigData destructor" << std::endl;
}
// 假设还有其他成员变量和复杂逻辑
};
const BigData& createBigData() {
static BigData data;
return data;
}
避免不必要拷贝的原理
- 返回值优化:当函数返回一个对象时,通常会进行拷贝构造。但是当返回一个常量引用时,不需要创建临时对象来存储返回值,直接返回对已有对象的引用。在上述例子中,
createBigData
函数返回的是data
的常量引用,调用者直接使用这个引用,而不是对data
进行拷贝。 - 提高效率:对于复杂数据结构
BigData
,拷贝构造和析构可能非常耗时。返回常量引用避免了这些不必要的操作,提高了函数调用的效率。
可能存在的风险
- 悬空引用:如果返回的对象在函数结束后被销毁,那么返回的引用将指向已释放的内存,成为悬空引用。例如,如果
createBigData
函数中data
不是static
的局部对象,函数结束时data
被销毁,返回的引用将无效。 - 多线程问题:如果在多线程环境下使用,
static
对象的初始化可能会出现竞争条件。例如,多个线程同时调用createBigData
函数,可能导致data
被多次初始化。
规避风险的方法
- 避免局部对象返回引用:确保返回的引用指向的对象生命周期足够长。如上述代码中使用
static
对象,其生命周期贯穿整个程序。 - 线程安全初始化:对于
static
对象在多线程环境下的初始化问题,可以使用C++11引入的局部static
变量的线程安全初始化机制。或者使用双检查锁定(Double-Checked Locking)等模式来确保对象只被初始化一次。例如:
class BigData {
public:
BigData() {
std::cout << "BigData constructor" << std::endl;
}
~BigData() {
std::cout << "BigData destructor" << std::endl;
}
};
class Singleton {
public:
static const BigData& getInstance() {
static std::once_flag flag;
static BigData instance;
std::call_once(flag, []() {
new (&instance) BigData();
});
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
这里使用std::once_flag
和std::call_once
确保BigData
对象只被初始化一次,避免了多线程环境下的竞争条件。