单例模式的实现--C++

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
实现单例模式
只能生成一个实例的类是实现了单例模式的类型
//(1)只适合单线程环境的的单例模式实现
class Singleton
{
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
 
private:
    static Singleton* instance;
 
public:
    static Singleton* Instance()
    {
        if (instance == nullptr)          //这个地方多线程是时候有可能出现错误。
        {
            instance = new Singleton();
        }
 
        return instance;
    };
};
 
Singleton* Singleton::instance = nullptr;
 
//以上的范例只适合在单线程下操作,如果在多线程中多个线程同时运行到判断instance
//是否为nullptr的if语句,并且instance的确没有创建时,那么多个线程都会创建一个
//一个实例,此时类型Singleton就不再满足单例模式的需求了。且此类型Singleton的写法
//会存在内存泄露的问题。
 
//针对内存泄露的问题,我们有以下两种解决方法。
//(1.1)解决内存泄露的第一种方式:智能指针解决内存泄露问题
#include<memory>
 
using namespace std;
 
class Singleton
{
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
 
private:
    static shared_ptr<Singleton> instance;
 
public:
    static shared_ptr<Singleton> Instance()
    {
        if (instance == nullptr)          //这个地方多线程是时候有可能出现错误。
        {
            instance = make_shared<Singleton>();
        }
 
        return instance;
    };
};
 
shared_ptr<Singleton> Singleton::instance = nullptr;
//将instance定义为智能指针的形式,则不会出现内存泄露的问题。
//(1.2) 解决内存泄露的第二种方式,利用类中类和静态成员结合的方式
class Singleton
{
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
 
private:
    static Singleton* instance;
 
private:
    class Deletor
    {
    public:
        ~Deletor()
        {
            if (Singleton::instance != nullptr)
            {
                delete Singleton::instance;
                Singleton::instance = nullptr;
            }
        }
    };
    static Deletor deletor;
 
public:
    static Singleton* Instance()
    {
        if (instance == nullptr)          //这个地方多线程是时候有可能出现错误。
        {
            instance = new Singleton();
        }
 
        return instance;
    };
};
 
Singleton* Singleton::instance = nullptr;
 
//这样在程序运行结束的时候,会调用deletor的析构函数,
//该函数会删除单例的唯一的实例。
 
#include<mutex>
 
using namespace std;
 
class Singleton
{
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
 
private:
    static Singleton* instance;
    static mutex my_mutex;
 
private:
    class Deletor
    {
    public:
        ~Deletor()
        {
            if (Singleton::instance != nullptr)
            {
                delete Singleton::instance;
                Singleton::instance = nullptr;
            }
        }
    };
    static Deletor deletor;
 
public:
    static Singleton* Instance()
    {
        my_mutex.lock(); //对这一部分的代码进行加锁,以免创建多个对象
        if (instance == nullptr)
        {
            instance = new Singleton();
        }
        my_mutex.unlock();
 
        return instance;
    };
};
 
Singleton* Singleton::instance = nullptr;
mutex Singleton::my_mutex;
 
//以上的方式可用于多线程的程序中,但是效率不是太高,因为
//在第一次创建了一个实例之后,之后每次使用接口,都需要再加锁。
//可以修改成以下的双判断加锁方式
 
#include<mutex>
 
using namespace std;
 
class Singleton
{
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
 
private:
    static Singleton* instance;
    static mutex my_mutex;
 
private:
    class Deletor
    {
    public:
        ~Deletor()
        {
            if (Singleton::instance != nullptr)
            {
                delete Singleton::instance;
                Singleton::instance = nullptr;
            }
        }
    };
    static Deletor deletor;
 
public:
    static Singleton* Instance()
    {
        if (instance == nullptr)
        {
            my_mutex.lock(); //对这一部分的代码进行双判断加锁,提高性能。
            //除了第一次使用接口,其他使用接口时再也不会进行加锁。
            if (instance == nullptr)
            {
                instance = new Singleton();
            }
            my_mutex.unlock();
        }
 
        return instance;
    };
};
 
Singleton* Singleton::instance = nullptr;
mutex Singleton::my_mutex;
 
//but,以上的代码仍然可能会存在问题。
//在某些内存模型中(虽然不常见)或者是由于编译器的优化以及运行时优化等等原因,
//使得instance虽然已经不是nullptr但是其所指对象还没有完成构造,
//这种情况下,另一个线程如果调用getInstance()就有可能使用到一个不完全初始化的对象。
 
//因为C++11规定了local static在多线程条件下的初始化行为,要求编译器保证内部静态变量的线程安全性。
//所以我们将代码修改成以下:
 
#include<mutex>
 
using namespace std;
 
class Singleton
{
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);
 
 
public:
    static Singleton& Instance()
    {
        static Singleton instance;
        return instance;
    };
};

  

posted on   xcxfury001  阅读(23)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示