单例设计模式 ⭐

定义

单例模式是一种创建型设计模式,它的核心思想是保证一个类只有一个实例,并提供一个全局访问点来访问这个实例。

优点

1.全局控制:保证只有一个实例,这样就可以严格的控制客户怎样访问它以及何时访问它,简单的说就是对唯一实例的受控访问。
2. 节省资源:也正是因为只有一个实例存在,就避免多次创建了相同的对象,从而节省了系统资源,而且多个模块还可以通过单例实例共享数据。
3. 懒加载:单例模式可以实现懒加载,只有在需要时才进行实例化,这无疑会提高程序的性能。

基本要求(规则)

  1. 私有的构造函数:防止外部代码直接创建类的实例。
  2. 私有的静态实例变量:保存该类的唯一实例。
  3. 公有的静态方法:通过公有的静态方法来获取类的实例。

种类

饿汉模式

不管是否需要使用这个实例,直接先创建好实例,然后当需要使用的时候,直接调方法就可以使用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton
{
private:
// 静态成员变量在类加载时初始化
static Singleton instance;
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton& getInstance() {
return instance;
}
}
// 在类外部初始化静态成员变量
Singleton Singleton::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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 版本一:单线程懒汉模式
class lazySingleton
{
private:
static lazySingleton* instance;
private:
lazySingleton() {}
~lazySingleton() {}
lazySingleton(const lazySingleton&) = delete;
lazySingleton& operator=(const lazySingleton&) = delete;
public:
static lazySingleton* getInstance()
{
if(instance == NULL)
instance = new lazySingleton();
return instance;
}
};
lazySingleton* lazySingleton::instance = NULL;

// 版本二:多线程懒汉模式
class lazySingleton {
private:
lazySingleton() {}
~lazySingleton() {}

lazySingleton(const lazySingleton&) = delete; // 删除拷贝构造函数
lazySingleton& operator=(const lazySingleton&) = delete; // 删除拷贝赋值操作符

static lazySingleton* instance;
static std::mutex mtx;

public:
static lazySingleton* getInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new lazySingleton();
}
return instance;
}
};

lazySingleton* lazySingleton::instance = nullptr;
std::mutex lazySingleton::mtx;

// 版本三:Meyers' Singleton
//利用了C++11及更高版本中静态局部变量的线程安全初始化特性
//即:局部静态变量不仅只会初始化一次,而且还是线程安全的。
class lazySingleton
{
private:
lazySingleton() = default;
~lazySingleton() = default;
lazySingleton(const lazySingleton&) = delete;
lazySingleton& operator=(const lazySingleton&) = delete;
public:
static lazySingleton& getInstance()
{
static lazySingleton instance;
return instance;
}
};
// 版本四:使用std::call_once和std::once_flag
class lazySingleton
{
public:
static lazySingleton& getInstance()
{ static std::once_flag flag;
std::call_once(flag, [&]() {instance.reset(new Singleton()); });
return *instance;
}

private:
static std::unique_ptr<lazySingleton> instance;

private:
lazySingleton() = default;
~lazySingleton() = default;
lazySingleton(const lazySingleton& other) = delete;
lazySingleton& operator=(const lazySingleton&) = delete;
};
std::unique_ptr<lazySingleton> lazySingleton::instance;

单例模式的应用场景

  1. 资源共享:当多个模块或系统需要共享某一资源时,可以使用单例模式确保该资源只被创建一次,避免重复创建和浪费资源。
  2. 控制资源访问:单例模式可以用于控制对特定资源的访问,例如数据库连接池、线程池等。
  3. 配置管理器:当整个应用程序需要共享一些配置信息时,可以使用单例模式将配置信息存储在单例类中,方便全局访问和管理。
  4. 日志记录器:单例模式可以用于创建一个全局的日志记录器,用于记录系统中的日志信息。
  5. 线程池:在多线程环境下,使用单例模式管理线程池,确保线程池只被创建一次,提高线程池的利用率。
  6. 缓存:单例模式可以用于实现缓存系统,确保缓存只有一个实例,避免数据不一致性和内存浪费。

应用场景举例:

  1. 网站计数器。
  2. 应用程序的日志应用。
  3. Web项目中的配置对象的读取。
  4. 数据库连接池。
  5. 多线程池。

使用场景总结:

  1. 频繁实例化然后又销毁的对象,使用单例模式可以提高性能。
  2. 经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏。
  3. 使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信。