在现代 C++(C++11 及以后)中,线程安全的懒汉式单例实现可以利用函数静态变量的初始化特性。该特性保证了无论多少线程同时访问该函数,编译器都会保证静态对象只会被初始化一次,并且在多线程环境下的初始化过程是线程安全的。下面给出一个完整的实现示例,并对关键点进行详细说明。
// Singleton.hpp
#ifndef SINGLETON_HPP
#define SINGLETON_HPP
#include <iostream>
#include <string>
class Singleton {
public:
// 删除拷贝构造函数和赋值运算符,防止多实例
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 公开一个获取实例的静态成员函数
static Singleton& instance() {
static Singleton instance; // 函数静态对象
return instance;
}
// 示例业务函数
void doSomething(const std::string& msg) {
std::cout << "Singleton says: " << msg << std::endl;
}
private:
// 构造函数私有化,防止外部直接实例化
Singleton() {
std::cout << "Singleton constructed." << std::endl;
}
~Singleton() {
std::cout << "Singleton destructed." << std::endl;
}
};
#endif // SINGLETON_HPP
// main.cpp
#include "Singleton.hpp"
#include <thread>
#include <vector>
void threadFunc(int id) {
Singleton& s = Singleton::instance();
s.doSomething("Hello from thread " + std::to_string(id));
}
int main() {
const int threadCount = 10;
std::vector<std::thread> threads;
threads.reserve(threadCount);
for (int i = 0; i < threadCount; ++i) {
threads.emplace_back(threadFunc, i);
}
for (auto& t : threads) {
t.join();
}
// 主线程也可以访问实例
Singleton::instance().doSomething("Hello from main thread");
return 0;
}
关键点说明
-
函数静态对象
static Singleton instance;在instance()函数内定义。C++11 起,标准保证在多线程环境下该对象的初始化是互斥的,避免了“双重检查锁定(Double-Checked Locking)”的复杂实现。 -
私有构造函数
通过将构造函数私有化,阻止外部直接创建对象,确保所有访问都必须经过instance()函数。 -
删除拷贝/赋值
Singleton(const Singleton&) = delete;与Singleton& operator=(const Singleton&) = delete;防止对象被复制或移动,保持单例唯一性。 -
资源释放
在程序结束时,函数静态对象会在main()结束后析构。若需要在程序运行期间主动销毁实例,可将单例包装在std::unique_ptr或使用std::shared_ptr并在需要时手动重置。 -
可扩展性
若单例需要依赖参数初始化,可使用std::call_once与std::once_flag或在第一次调用instance()时延迟构造。
性能与可维护性
- 延迟加载:首次调用
instance()时才构造对象,减少启动时资源占用。 - 线程安全:标准保证,无需手动加锁,代码更简洁、错误更少。
- 可测试性:由于单例是全局可访问,可在单元测试中使用 Mock 对象替换,实现更好的可测试性。
进一步阅读
- 《Effective Modern C++》 – Scott Meyers,讨论 C++11 后的单例实现细节
- 《C++ Concurrency in Action》 – Anthony Williams,深入多线程与同步原语
- C++ 标准文档(ISO/IEC 14882:2017)第3.6.3节关于静态对象初始化的规范
通过以上实现,你可以在任何需要全局唯一实例的场景下安全、简洁地使用单例模式。