单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理

饿汉模式:不管将来用不用,程序启动时就创建一个唯一的实例对象
实现简单,如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,使用饿汉模式来避免资源竞争,提高响应速度。但是可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定,而且由于单例对象是在main函数之前创建的,所以单例类中不可创建线程、使用动态库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
public:
static Singleton* GetInstance() {
return &m_instance;
}
private:
// 构造函数私有
Singleton() {};
// 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化

懒汉模式:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊,初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Singleton {
public:
// 需要设置volatile关键字,否则可能被编译器优化
volatile static Singleton* GetInstance() {
// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
if (_pInstance == nullptr) { // 如果_pInstance已经为空就不需要加锁
// _mtx.lock();
{
std::unique_lock<std::mutex> lock(_mtx);
if (_pInstance == nullptr) {
_pInstance = new Singleton();
}
}
// _mtx.unlock();
}
return _pInstance;
}

// 手动释放单例对象
static void DelInstance() {
std::unique_lock<std::mutex> lock(_mtx);
delete _pInstance;
_pInstance = nullptr;
}

// 实现一个内嵌垃圾回收类,程序结束时候正常释放单例对象
class CGarbo {
public:
~CGarbo() {
if (_pInstance) {
delete _pInstance;
_pInstance = nullptr;
}
}
};

// 防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
private:
// 构造函数私有
Singleton() {};

// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
volatile static Singleton* _pInstance;
static std::mutex _mtx;
};
volatile Singleton* Singleton::_pInstance = nullptr;
Singleton::CGarbo Garbo;
std::mutex Singleton::_mtx;