c++ 单例模式

记录一下 c++ 单例模式的实现方式。

饿汉模式

c++11 以后饿汉单例最好的方法是使用局部定义的 static 变量,因为 c++11 标准保证多线程初始化局部静态变量是线程安全的。

非模板的实现

GetInstance() 静态方法中定义一个局部静态变量,返回其指针即可。注意要把构造函数声明为私有,同时删除其拷贝构造函数、移动构造函数、拷贝赋值运算符、移动赋值运算符,保证不会以除 GetInstance() 方法以外的方式构造对象。

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
#include <iostream>

class Singleton {
public:
static Singleton* GetInstance() {
static Singleton instance;
return &instance;
}

private:
Singleton() = default;
Singleton& operator=(const Singleton&) = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
Singleton(Singleton&&) = delete;
};

int main() {
Singleton* s1 = Singleton::GetInstance();
std::cout << s1 << std::endl;

Singleton* s2 = Singleton::GetInstance();
std::cout << s2 << std::endl;

return 0;
}

模板实现

这里 Singleton 类的 GetInstance() 方法在构造 A 的局部静态变量的时候要用到 A 的构造函数,而 A 的构造函数已被声明为私有,故需要声明 Singleton 为 A 的友元类。

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
#include <iostream>

template<typename T>
class Singleton {
public:
static T* GetInstance() {
static T instance;
return &instance;
}

private:
Singleton() = default;
Singleton& operator=(const Singleton&) = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
Singleton(Singleton&&) = delete;
};

class A {
friend class Singleton<A>;

private:
A() = default;
A& operator=(const A&) = delete;
A(const A&) = delete;
A& operator=(A&&) = delete;
A(A&&) = delete;
};

int main() {
A* s1 = Singleton<A>::GetInstance();
std::cout << s1 << std::endl;

A* s2 = Singleton<A>::GetInstance();
std::cout << s2 << std::endl;

return 0;
}

懒汉模式

使用 std::call_once 配合 std::once_flag 保证多线程下初始化单例指针的线程安全。注意使用 std::call_once 在 GCC 环境下需要加 -pthread 编译选项,否则运行时会抛异常,使用旧版的 -lpthread 选项也是不行的。关于 -pthread 和 -lpthread 区别参考这篇文章

非模板实现

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
#include <iostream>
#include <thread>
#include <mutex>
#include <cassert>

class Singleton {
public:
static Singleton* GetInstance() {
static std::once_flag s_flag;
std::call_once(s_flag, []() {
instance = new Singleton;
});
return instance;
}

private:
Singleton() = default;
Singleton& operator=(const Singleton&) = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
Singleton(Singleton&&) = delete;

static Singleton* instance;
};

Singleton* Singleton::instance = nullptr;

int main() {
Singleton* instances[100] = {nullptr};
std::thread threads[100];
for(int i = 0; i < 100; i++) {
threads[i] = std::thread([i, &instances]() {
instances[i] = Singleton::GetInstance();
});
}

for(int i = 0; i < 100; i++) {
threads[i].join();
}

Singleton* ins = instances[0];
for(int i = 1; i < 100; i++) {
assert(instances[i] == ins);
}

return 0;
}

模板实现

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
#include <iostream>
#include <thread>
#include <mutex>
#include <cassert>

template<typename T>
class Singleton {
public:
static T* GetInstance() {
static std::once_flag s_flag;
std::call_once(s_flag, []() {
instance = new T;
});
return instance;
}

private:
Singleton() = default;
Singleton& operator=(const Singleton&) = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
Singleton(Singleton&&) = delete;

static T* instance;
};

template<typename T>
T* Singleton<T>::instance = nullptr;

class A {
friend class Singleton<A>;

private:
A() = default;
A(const A&) = delete;
A(A&&) = delete;
A& operator=(const A&) = delete;
A& operator=(A&&) = delete;
};

int main() {
A* instances[100] = {nullptr};
std::thread threads[100];
for(int i = 0; i < 100; i++) {
threads[i] = std::thread([i, &instances]() {
instances[i] = Singleton<A>::GetInstance();
});
}

for(int i = 0; i < 100; i++) {
threads[i].join();
}

A* ins = instances[0];
for(int i = 1; i < 100; i++) {
assert(instances[i] == ins);
}

return 0;
}