设计模式——单例

目录

 

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值