C++设计模式之单例模式

单例模式:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

 

应用场景:
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性。
一个全局使用的类频繁地创建与销毁。

比如:windows 资源管理器,回收站等。

这应该是类设计者的职责,而不是使用者的职责。也就是说这个单例不应该由人来控制,而应该由代码来限制,强制单例。

 

 

 

 

 1 #include <iostream>
 2 #include <mutex>
 3 #include <thread>
 4 using namespace std;
 5 mutex mu;//线程互斥对象
 6 class Singleton {
 7 public:
 8     // 静态方法,供全局调用
 9     static Singleton* getInstance() ;
10     void GetName() { cout << "我是单例" << endl; }
11 private:
12     //私有构造函数,不允许使用者自己生成对象
13     Singleton() 
14     {
15         cout << "++++++Singleton被构造+++++" << endl; 
16     } 
17     Singleton(const Singleton& other);
18     static Singleton* m_instance; //静态成员变量 
19 };

 

 

单例模式分为两种类型:饿汉式,懒汉式。

饿汉式:

1 // 饿汉式
2 Singleton* Singleton::getInstance()
3 {
4     return m_instance;
5 }
6 // 静态成员需要先初始化
7 Singleton* Singleton::m_instance = new Singleton();

在访问量比较大,或者可能访问的线程比较多时,可以实现更好的性能,以空间换时间。
因为是静态属性在单例类定义的时候就进行实例化,所以优点是线程是安全的,缺点是无论用户是否使用单例对象都会创建单例对象。

 

懒汉式:

 1 //静态成员需要先初始化
 2 Singleton* Singleton::m_instance = NULL;
 3 // 懒汉式
 4 Singleton* Singleton::getInstance()
 5 {
 6 // 先检查对象是否存在
 7 if (m_instance == NULL)
 8        m_instance = new Singleton();
 9 return m_instance;
10 }

在第一次调用getInstance()的时候实例化,不调用就不会生成对象,不占据内存。适合在访问量较小时,以时间换空间。

 懒汉式是线程不安全的,比如两个线程同时运行到上面的第7行后,那么两个将分别创建两个实例,不满足单例的要求。

为解决线程不安全,有了下面的加锁处理:

1 // 懒汉式  加锁(代价高)
2 Singleton* Singleton::getInstance()
3 {
4     mu.lock();
5     if (m_instance == NULL)
6         m_instance = new Singleton();
7     mu.unlock();
8     return m_instance;
9 }

但因为每次调用该方法时都要进行加锁,代价太高,因而出现了著名的双检查锁:

 1 //   懒汉式 双检查锁
 2 Singleton* Singleton::getInstance()
 3 {
 4     if (m_instance == NULL)    
 5     {
 6         mu.lock();
 7         if (m_instance == NULL)
 8             m_instance = new Singleton();
 9         mu.unlock();
10     }
11     return m_instance;
12 }

近乎完美。

但,由于编译器等进行优化,导致内存读写存在reorder,有时导致双检查锁的不安全性,处理办法:

 1 //C++ 11版本之后的跨平台实现 
 2 // atomic c++11中提供的原子操作
 3 #include <atomic>
 4 #include <mutex>
 5    
 6 class Singleton {
 7 public:
 8        // 静态方法,供全局调用
 9        static Singleton* getInstance();
10        void GetName() { cout << "我是单例" << endl; }
11 private:
12        //私有构造函数,不允许使用者自己生成对象
13        Singleton() { cout << "Singleton被构造" << endl; }
14        Singleton(const Singleton& other);
15        static atomic<Singleton*> m_instance; //静态成员变量 
16        static mutex m_mutex;
17 };
18 std::atomic<Singleton*> Singleton::m_instance;
19 std::mutex Singleton::m_mutex;
20 /*
21 * std::atomic_thread_fence(std::memory_order_acquire);
22 * std::atomic_thread_fence(std::memory_order_release);
23 * 这两句话可以保证他们之间的语句不会发生乱序执行。
24 */
25 Singleton* Singleton::getInstance() {
26        Singleton* tmp = m_instance.load(std::memory_order_relaxed);
27        std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
28        if (tmp == nullptr) {
29            std::lock_guard<std::mutex> lock(m_mutex);
30            tmp = m_instance.load(std::memory_order_relaxed);
31            if (tmp == nullptr) {
32                tmp = new Singleton;
33                std::atomic_thread_fence(std::memory_order_release);//释放内存fence
34                m_instance.store(tmp, std::memory_order_relaxed);
35            }
36        }
37        return tmp;
38 }

最后是main方法

 1 void test01()
 2 {
 3     Singleton *pSingleton = Singleton::getInstance();
 4     pSingleton->GetName();
 5     Singleton *pSingleton1 = Singleton::getInstance();
 6     pSingleton1->GetName();
 7 }
 8 
 9 void test02()
10 {
11     Singleton *pSingleton = Singleton::getInstance();
12     pSingleton->GetName();
13 }
14 
15 
16   // 多线程调用
17 void thread01()
18 {
19     for (int i = 0; i < 5; i++)
20     {
21         //cout << "--thread01 working...." << endl;
22         Singleton *lazy1 = Singleton::getInstance();
23         //cout << "--thread01创建单例lazy1地址:" << lazy1 << endl;
24     }
25 }
26 void thread02()
27 {
28     for (int i = 0; i < 5; i++)
29     {
30         //cout << "--thread02 working...." << endl;
31         Singleton *lazy2 = Singleton::getInstance();
32         //cout << "--thread02创建单例lazy2地址:" << lazy2 << endl;
33     }
34 }
35 
36 int main()
37 {
38 //     test01();
39 //     test02();
40 
41     thread thread1(thread01);
42     thread thread2(thread01);
43     thread1.detach();
44     thread2.detach();
45     for (int i = 0; i < 5; i++)
46     {
47         //cout << "--Main thread working..." << endl;
48         Singleton *main = Singleton::getInstance();
49         //cout << "--Main 创建单例lazy地址:" << main << endl;
50     }
51 
52     system("pause");
53     return 0;
54 }

 

优点: 
在系统内存中只存在一个对象,因此可以节约系统的资源,对于一些需要频繁创建和销毁的对象,使用单例模式无疑是提高了系统的性能;
单例模式允许可变数目的实例。基于单例模式可以进行扩展,使用与控制单例对象相似的方法获得指定个数的实例对象,既节约了系统资源,又解决了由于单例对象共享过多有损性能的问题(自行提供指定数目实例对象的类可成为多例类) 。
缺点:
由于单例模式没有抽象层,所以扩展起来很难;
单例类职责过重,在一定程度上违背了单一职责原则。因为单例类既提供了业务方法,又提供了创建对象的方法(工厂方法),将对象的创建和对象本身的功能耦合在一起;
不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态;
多线程模式下较复杂。

 

posted @ 2020-04-04 11:03  自恋狂学长  阅读(501)  评论(0编辑  收藏  举报