单例模式(C++编码)

一、什么是单例模式

单例模式(Singleton Pattern)是使用最广泛的设计模式之一,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。单例模式常用于资源管理,例如日志、线程池。

定义一个单例类大致分为如下三步:

1、私有化它的构造函数,以防止外界创建单例类的对象;(单例类只能提供私有的构造函数,即保证不能随意创建该类的实例)

2、使用类的私有静态指针变量指向类的唯一实例;(因为构造函数是私有的,其他对象不能创建单例类的实例,只能是单例类自己来创建)

3、使用一个公有的静态方法获取该实例。(由于该类的构造函数是私有的,外界无法通过new去获取它的实例,那么就必须提供一个静态的公有方法,该方法创建或者获取它本身的静态私有对象并返回)

二、单例模式实现方案(C++编码)

1、单例的两种实现方式

懒汉式:故名思义,懒汉很懒只有饿了才会去找吃的。也就是说,只有在需要使用的时候才会去实例化。

饿汉式:饿了肯定要饥不择食。在单例类定义的时候就进行实例化。

2、懒汉式单例模式

简单的懒汉式单例模式编码如下:

 1 // Singleton.h
 2 #pragma once
 3  4 class Singleton
 5 {
 6 public:
 7     static Singleton *GetInstance();
 8  9 private:
10     Singleton();
11     Singleton(const Singleton &);
12     Singleton& operator=(const Singleton &);
13 14     static Singleton *s_instance;
15 };
16 17 // Singleton.cpp
18 #include <iostream>
19 #include "Singleton.h"
20 21 Singleton *Singleton::s_instance = NULL;
22 23 Singleton::Singleton() {}
24 Singleton::Singleton(const Singleton &) {}
25 Singleton& Singleton::operator=(const Singleton &) {}
26 27 Singleton* Singleton::GetInstance()
28 {
29     if(nullptr == s_instance)
30     {
31         s_instance = new Singleton();
32     }
33     return s_instance;
34 }

(1)默认构造函数是私有的,外部不能进行单例类的实例化;

(2)拷贝构造函数和赋值运算符也是私有的,以禁止拷贝和赋值;

(3)具有一个私有的静态成员指针 s_instance,指向唯一的实例;

(4)提供一个公有的静态成员函数用于返回实例,如果实例为 NULL,则进行实例化。

3、饿汉式单例模式

简单的饿汉式单例模式编码如下:

 1 // Singleton.h
 2 #pragma once
 3  4 class Singleton
 5 {
 6 public:
 7     static Singleton *GetInstance();
 8  9 private:
10     Singleton();
11     Singleton(const Singleton &);
12     Singleton &operator=(const Singleton &);
13 14     static Singleton *s_instance;
15 };
16 17 // Singleton.cpp
18 #include <iostream>
19 #include "Singleton.h"
20 21 Singleton *Singleton::s_instance = new Singleton();
22 23 Singleton::Singleton() {}
24 Singleton::Singleton(const Singleton &) {}
25 Singleton& Singleton::operator=(const Singleton &) {}
26 27 Singleton* Singleton::GetInstance()
28 {
29     return s_instance;
30 }

说明:与懒汉式单例模式不同之处是,饿汉式在全局作用域进行单例类的实例化,并用此实例初始化单例类的静态成员指针 s_instance。

4、线程安全问题

懒汉式:如果有两个线程同时获取单例类的实例,都发现实例不存在,则都会进行实例化,就会产生两个实例都要赋值给 s_instance,这是是非常不安全的,因而考虑为实例化过程加锁。

 1 Singleton* Singleton::GetInstance()
 2 {
 3     lock();
 4     if(nullptr == s_instance)
 5     {
 6         s_instance = new Singleton();
 7     }
 8     unlock();
 9     
10     return s_instance;
11 }

但这个获取实例的方法存在性能问题,每次获取实例的时候都要先上锁,然后再解锁,如果有很多线程的话会造成大量线程的阻塞,改进成双检锁后编码如下:

 1 Singleton* Singleton::GetInstance()
 2 {
 3     if(nullptr == s_instance)
 4     {
 5          lock();
 6          if(nullptr == s_instance)
 7          {
 8             s_instance = new Singleton();
 9          }
10          unlock();
11     }
12     return s_instance;
13 }

绝大多数情况下,获取实例时都是直接返回实例,这时候不会涉及到上锁、解锁的问题。只有在没有实例的时候,才会涉及上锁、解锁,这种情况是很少的。这个获取实例的方法对性能几乎无影响。

局部静态初始化方法(或称之为 Meyers' Singleton,C++11标准):

 1 class Singleton
 2 {
 3 public:
 4     static Singleton& GetInstance()
 5     {
 6         static Singleton instance;
 7         return instance;
 8     }
 9     
10 private:
11     Singleton() {}
12     Singleton(const Singleton &) {}
13     Singleton& operator=(const Singleton &) {}
14 };

饿汉式:程序运行初期就进行了单例类实例化,不存在上述的线程安全问题。

5、对象释放问题

一般情况下,单例类的实例都是常驻内存的,一直存在于进程的生命周期,因此不需要手动释放。如果的确需要释放实例占用的内存,一定不能在单例类的析构函数中进行delete操作,这样会造成无限循环,可以考虑增加一个 destroy 方法用于释放内存(或使用智能指针),或者在单例类中定义一个内嵌的垃圾回收类,下面给出垃圾回收类的简单实现。

 1 // Singleton.h
 2 #pragma once
 3  4 class Singleton
 5 {
 6 public:
 7     static Singleton *GetInstance();
 8  9 private:
10     class CGarbo
11     {
12     public:
13         ~CGarbo()
14         {
15             if(nullptr != Singleton::s_instance)
16             {
17                 delete Singleton::s_instance;
18             }
19         }
20     };
21 22     static CGarbo s_gb;
23 24 private:
25     Singleton();
26     Singleton(const Singleton &);
27     Singleton& operator=(const Singleton &);
28 29 private:
30     static Singleton *s_instance;
31 };
32 33 // Singleton.cpp
34 #include <iostream>
35 #include "Singleton.h"
36 37 Singleton *Singleton::s_instance = NULL;
38 39 Singleton::Singleton() {}
40 Singleton::Singleton(const Singleton &) {}
41 Singleton& Singleton::operator=(const Singleton &) {}
42 43 Singleton *Singleton::GetInstance()
44 {
45     if (nullptr == s_instance)
46     {
47         lock();
48         if(nullptr == s_instance)
49         {
50             s_instance = new Singleton();
51         }
52         unlock();
53     }
54 55     return s_instance;
56 }

三、Reference

C++单例(知乎):https://zhuanlan.zhihu.com/p/37469260

C++单例(博客园):https://www.cnblogs.com/leaves1024/p/10985599.html

盘点C++几种常见的设计模式及具体实现:https://zhuanlan.zhihu.com/p/321939154

 

posted @ 2021-10-22 15:18  HOracle  阅读(318)  评论(0编辑  收藏  举报