在这个优化版本中,我们将重点放在最推荐的两种方法上:使用 std::call_once 和局部静态变量。同时,我们会讨论一些额外的考虑点,以使单例模式更加健壮和易于使用。
- 使用 std::call_once 实现
#include <iostream>
#include <mutex>
#include <memory>
class Singleton {
private:
Singleton() { std::cout << "Singleton constructed." << std::endl; }
static std::once_flag initInstanceFlag;
static std::unique_ptr<Singleton> instance;
// 删除拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
std::call_once(initInstanceFlag, []() {
instance.reset(new Singleton());
});
return *instance;
}
void someMethod() {
std::cout << "Method of the singleton" << std::endl;
}
~Singleton() {
std::cout << "Singleton destructed." << std::endl;
}
};
std::once_flag Singleton::initInstanceFlag;
std::unique_ptr<Singleton> Singleton::instance;
// 使用示例
void threadFunction() {
Singleton& singleton = Singleton::getInstance();
singleton.someMethod();
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
- 使用局部静态变量实现(C++11及以后)
#include <iostream>
#include <memory>
class Singleton {
private:
Singleton() { std::cout << "Singleton constructed." << std::endl; }
// 删除拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void someMethod() {
std::cout << "Method of the singleton" << std::endl;
}
~Singleton() {
std::cout << "Singleton destructed." << std::endl;
}
};
// 使用示例
void threadFunction() {
Singleton& singleton = Singleton::getInstance();
singleton.someMethod();
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
优化说明:
-
使用 std::unique_ptr 替代原始指针:这样可以自动管理内存,防止内存泄漏。
-
将构造函数设为私有:确保只能通过 getInstance() 方法创建实例。
-
删除拷贝构造函数和赋值操作符:防止通过拷贝或赋值创建新实例。
-
返回引用而不是指针:简化使用,并确保用户不能删除单例实例。
-
在 std::call_once 版本中使用 lambda 函数:使代码更加简洁。
-
移除了显式的线程创建,改为使用函数来演示多线程环境。
这两种方法都是线程安全的,并且都有各自的优点:
- std::call_once 方法:显式控制初始化过程,适合需要延迟初始化的场景。
- 局部静态变量方法:代码最简洁,由C++11标准保证线程安全,适合大多数场景。
选择哪种方法主要取决于你的具体需求和偏好。局部静态变量方法通常是最推荐的,因为它简单、安全、有效。
注意事项:
- 单例模式虽然有用,但应谨慎使用,因为它引入了全局状态,可能使代码难以测试和维护。
- 在某些情况下,依赖注入可能是更好的选择,它提供了更好的灵活性和可测试性。
通过这些优化,我们创建了一个更加健壮、安全和易于使用的线程安全单例模式实现。这些实现充分利用了C++11的特性,提供了良好的性能和线程安全保证。