设计模式——单例

tech2022-11-27  91

目录

 

1. 懒汉式单例

1.1 单线程的懒汉式单例

1.2 多线程的懒汉式单例

1.3 使用静态局部变量的多线程懒汉式单例

2. 饿汉式单例


1. 懒汉式单例

懒汉式单例在第一次被使用的时候才会进行实例化。

1.1 单线程的懒汉式单例

#include <iostream> using namespace std; class Singleton { private: Singleton() { cout << "Construct called!" << endl; } Singleton(Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; public: ~Singleton() { cout << "Destructor called!" << endl; } static Singleton* getInstance() { if (nullptr == m_instance_ptr) { m_instance_ptr = new Singleton; } return m_instance_ptr; } private: static Singleton* m_instance_ptr; }; Singleton* Singleton::m_instance_ptr = nullptr; int main() { Singleton* instance = Singleton::getInstance(); Singleton* instance2 = Singleton::getInstance(); return 0; }

上面这个单线程的单例问题比较多,首先会存在内存泄漏,然后再多线程环境下,可能有多个线程同时判断“if (nullptr == m_instance_ptr)”为真,然后进入下面的语句,可能导致创建了多个实例。

对于内存泄漏的问题,我们进行如下修改:

class Singleton { private: Singleton() { cout << "Construct called!" << endl; } Singleton(Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; //Singleton退出时会自动析构gc,调用GC的析构函数, //我们在GC的析构函数中释放m_instance_ptr class GC { public: ~GC() { if (m_instance_ptr != nullptr) { delete m_instance_ptr; m_instance_ptr = nullptr; } } }; public: ~Singleton() { cout << "Destructor called!" << endl; } static Singleton* getInstance() { if (nullptr == m_instance_ptr) { m_instance_ptr = new Singleton; } return m_instance_ptr; } private: static Singleton* m_instance_ptr; static GC gc; }; Singleton* Singleton::m_instance_ptr = nullptr; Singleton::GC Singleton::gc;

在Singleton类中增加了一个GC嵌套类,并声明了一个static类型的GC的变量。当Singleton结束的时候会调用GC类的析构函数,而在其析构函数中会使用delete释放m_instance_ptr指向的内存,由此可以避免内存泄漏。

但是该方法在多线程环境下仍然是不安全的。

1.2 多线程的懒汉式单例

在多线程情况下,需要对共享资源进行加锁,以保证其互斥访问。我们可以再结合C++2.0的智能指针,将上述代码修改一下。

#include <iostream> #include <mutex> #include <memory> using namespace std; class Singleton { private: Singleton() { cout << "Construct called!" << endl; } Singleton(Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; public: typedef shared_ptr<Singleton> Ptr; ~Singleton() { cout << "Destructor called!" << endl; } static Ptr getInstance() { if (nullptr == m_instance_ptr) { lock_guard<mutex> lk(m_mutex); if (nullptr == m_instance_ptr) { m_instance_ptr = Ptr(new Singleton); } } return m_instance_ptr; } private: static Ptr m_instance_ptr; static mutex m_mutex; }; Singleton::Ptr Singleton::m_instance_ptr = nullptr; mutex Singleton::m_mutex; int main() { Singleton::Ptr instance = Singleton::getInstance(); Singleton::Ptr instance2 = Singleton::getInstance(); }

使用智能指针shard_ptr,解决了内存泄漏的问题,使用互斥量mutex和锁管理器lock_guard实现了线程安全。

1.3 使用静态局部变量的多线程懒汉式单例

C++2.0保证了变量在初始化的时候如果并发同时进入了声明语句,并发线程会阻塞等待声明结束。

这可以保证并发线程在获取静态局部变量的时候一定是初始化过的。

#include <iostream> using namespace std; class Singleton { private: Singleton() { cout << "Construct called!" << endl; } public: ~Singleton() { cout << "Destructor called!" << endl; } Singleton(Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance() { static Singleton instance; return instance; } }; int main() { Singleton& instance = Singleton::getInstance(); Singleton& instance2 = Singleton::getInstance(); }

2. 饿汉式单例

饿汉式单例,在加载时就进行初始化,而不是第一次引用时才进行初始化。因为是加载时就进行初始化,因此不存在多线程下创建多个实例的可能。

#include <iostream> using namespace std; class Singleton { private: Singleton() { cout << "Construct called!" << endl; } Singleton(Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; //Singleton退出时会自动析构gc,调用GC的析构函数, //我们在GC的析构函数中释放m_instance_ptr class GC { public: ~GC() { if (m_instance_ptr != nullptr) { delete m_instance_ptr; m_instance_ptr = nullptr; } } }; public: static Singleton* getInstance() { return m_instance_ptr; } ~Singleton() { cout << "Destructor called!" << endl; } private: static Singleton* m_instance_ptr; static GC gc; }; Singleton* Singleton::m_instance_ptr = new Singleton; Singleton::GC Singleton::gc; int main() { Singleton* instance = Singleton::getInstance(); Singleton* instance2 = Singleton::getInstance(); return 0; }

这里也可以使用智能指针来防止内存泄漏。

 

参考资料:C++ 单例模式总结与剖析

最新回复(0)