单例模式

如何正确实现一个单例模式?

单例模式

单例类的特点

  • 构造函数和析构函数为私有类型,目的是禁止外部构造和析构。
  • 拷贝构造函数和赋值构造函数是私有类型,目的是禁止外部拷贝和赋值,确保实例的唯一性。
  • 类中有一个获取实例的静态方法,可以全局访问。

Meyer‘s Singleton

要点:

  • static获取实例对象的引用
  • 静态函数局部变量作为单一实例
  • 懒汉式

懒汉式:系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。这种方式要考虑线程安全。

特点:静态局部变量方式天然线程安全。

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
class S
{
public:
// Guaranteed to be destroyed.
// Instantiated on first use.
static S &getInstance(){
static S instance;
return instance;
}

private:
S() {}

// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are inaccessible(especially from outside), otherwise, you may accidentally get copies of
// your singleton appearing.
S(S const &); // Don't Implement
void operator=(S const &); // Don't implement

// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
public:
S(S const &) = delete;
void operator=(S const &) = delete;

// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
};

加锁懒汉式实现

要点:

  • 静态获取实例指针的函数
  • 静态指针作为单一实例
  • 多线程适用
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
///  加锁的懒汉式实现  //
class SingleInstance
{
public:
static SingleInstance *GetInstance();
static void deleteInstance();

private:
// 将其构造和析构成为私有的, 禁止外部构造和析构
SingleInstance();
~SingleInstance();
// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
SingleInstance(const SingleInstance &signal);
const SingleInstance &operator=(const SingleInstance &signal);

private:
static SingleInstance* m_SingleInstance;
static std::mutex m_Mutex;
};

// 初始化静态成员变量
SingleInstance *SingleInstance::m_SingleInstance = nullptr;
std::mutex SingleInstance::m_Mutex;

// 注意:不能返回指针的引用,否则存在外部被修改的风险!
SingleInstance *SingleInstance::GetInstance()
{
// 这里使用了两个 if 判断语句的技术称为双检锁;
// 好处是:只有判断指针为空的时候才加锁,
// 避免每次调用 GetInstance的方法都加锁。
if (m_SingleInstance == nullptr){
std::unique_lock<std::mutex> lock(m_Mutex);
if (m_SingleInstance == nullptr){
volatile auto temp = new (std::nothrow) SingleInstance();
m_SingleInstance = temp;
}
}
return m_SingleInstance;
}

void SingleInstance::deleteInstance()
{
std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
if (m_SingleInstance){
delete m_SingleInstance;
m_SingleInstance = nullptr;
}
}

cpp11实现

要点:

  • call_once
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
#include <iostream>
#include <memory>
#include <mutex>

class Singleton {
public:
static std::shared_ptr<Singleton> getSingleton();

~Singleton() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}

private:
Singleton() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};

static std::shared_ptr<Singleton> singleton = nullptr;
static std::once_flag singletonFlag;

std::shared_ptr<Singleton> Singleton::getSingleton() {
std::call_once(singletonFlag, [&] {
singleton = std::shared_ptr<Singleton>(new Singleton());
});
return singleton;
}

饿汉式实现

系统一运行,就初始化创建实例,当需要时,直接调用即可。这种方式本身就线程安全。

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
// 饿汉实现 /
class Singleton
{
public:
static Singleton* GetInstance();
static void deleteInstance();
private:
// 将其构造和析构成为私有的, 禁止外部构造和析构
Singleton();
~Singleton();
// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
Singleton(const Singleton &signal);
const Singleton &operator=(const Singleton &signal);

private:
static Singleton *g_pSingleton;
};

// 代码一运行就初始化创建实例 ,本身就线程安全
Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton();

Singleton* Singleton::GetInstance()
{
return g_pSingleton;
}

void Singleton::deleteInstance()
{
if (g_pSingleton){
delete g_pSingleton;
g_pSingleton = nullptr;
}
}

reference

https://blog.csdn.net/unonoi/article/details/121138176

https://stackoverflow.com/questions/1008019/how-do-you-implement-the-singleton-design-pattern

作者

Desirer

发布于

2025-05-04

更新于

2025-05-04

许可协议