单例模式多种实现方式

本文详细介绍了几种单例模式的实现方式,包括饿汉模式和懒汉模式,并分析了它们的线程安全性和优缺点。通过静态对象、引用返回以及线程锁等方式确保单例的唯一性。同时,展示了模板化的单例模式以及如何使用线程函数`thread_once`来设计线程安全的单例。讨论了在不同场景下如何选择合适的单例实现策略。
摘要由CSDN通过智能技术生成

饿汉模式:

1。返回静态对象(唯一对象)地址

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return &instance;
	}
	~Singleton() {}
private:
	Singleton() {}
	Singleton(const Singleton& src) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton instance;
};
Singleton Singleton::instance; //饿汉式单例模式 一定是线程安全的


int main()
{
	Singleton* ps = Singleton::GetInstance();
	Singleton* pb = Singleton::GetInstance();
	// ps pb 指向同一个对对象,即类只生成一个实例
	return 0;
}

1。返回静态对象(唯一对象)引用

class Singleton
{
public:			// 使用引用返回更安全,我们无需对其检查是否为空引用
	static Singleton& GetInstance()
	{
		return instance;
	}
	~Singleton() {}
private:
	Singleton() {}
	Singleton(const Singleton& src) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton instance;	// 注意:这里只能是静态类型,否则会产生无限递归创建Singleton对象
								//		因为,静态类型不属于类对象,因此在创建对象时不会再次创建..
};
Singleton Singleton::instance; //饿汉式单例模式 一定是线程安全的

int main()
{
	Singleton& ps = Singleton::GetInstance();
	Singleton& pb = Singleton::GetInstance();
	// ps pb 指向同一个对对象,即类只生成一个实例
	return 0;
}

3.在静态函数中生成静态对象,注意这里是无参的生成静态对象。我们认为次方案是线程安全的。但是只生成一个实例没有意义,我们需要实际运用时必定需要传递参数,而一旦如此就会成为线程不安全的。

class Singleton
{
public:
	static Singleton& GetInstance()
	{
		static Singleton instance; // 线程安全 ?
		return instance;
	}
	~Singleton() {}
private:
	Singleton() {}
	Singleton(const Singleton& src) = delete;
	Singleton& operator=(const Singleton&) = delete;
};

int main()
{
	Singleton& ps = Singleton::GetInstance();
	Singleton& pb = Singleton::GetInstance();
	// ps pb 指向同一个对对象,即类只生成一个实例
	return 0;
}

线程不安全,函数内的静态变量,是全局变量。因此在第一次初始化时可能线程不安全。

#include <iostream>
class Singleton
{
public:
	static Singleton& GetInstance(int x)
	{
		static Singleton instance(x); // 线程安全 ?
		return instance;
	}
	~Singleton() {}
	int val;		// 便于演示,这里设置为public
private:
	Singleton(int x) :val(x){}
	Singleton(const Singleton& src) = delete;
	Singleton& operator=(const Singleton&) = delete;
};

int main()
{
	Singleton& ps = Singleton::GetInstance(10);
	Singleton& pb = Singleton::GetInstance(20);
	std::cout << ps.val << pb.val << std::endl;	// 10
	// 同一个线程中,只会是第一个生成的实例,但存在多个线程时值可能会被修改
	return 0;
}
优缺点

优点:程序加载时就进行实例化,之后的操作效率会很高。
缺点: 由于程序加载时就进行实例化,如果后续不对此类进行任何操作,就会导致内存的浪费。


饿汉模式

注,此方法一定线程不安全。前者线程安全是因为,程序开始执行时,单例对象已经创建完毕,而此时线程还未开始,不会有创建过个单例对象的可能。 而饿汉模式中,我们用到时才生成对象,如果两个或多个线程同时进行了这一步,存在多个线程同时访问临界区的情况。

class Singleton
{
private:
	static Singleton* instance;
	Singleton() {}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
public:
	~Singleton() {}
	static Singleton* GetInstance()
	{
		if (instance == nullptr)
		{	// 需要时才创建
			instance = new Singleton();
		}
		return instance;
	}
};
Singleton* Singleton::instance = nullptr;

上述方案不安全,因此我们需要加锁保证线程安全。

#include <mutex>
std::mutex mtx;
class Singleton
{
private:
	static Singleton* instance;
	Singleton() {}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
public:
	~Singleton() {}
	static Singleton* GetInstance()
	{
		if (instance == nullptr)
		{	// 需要时才创建
			std::lock_guard<std::mutex> lock(mtx);	// 加锁保证线程安全
			instance = new Singleton();
		}
		return instance;
	}
};
Singleton* Singleton::instance = nullptr;
优缺点

优点: 在第一次调用的时候才进行实例化。
缺点: 当多个线程同时进入到 if(instance == nullptr) {…} 时,会创建多个对象


单例模式模板,继承单例模式

#include <iostream>
template < class T>
class Singleton
{
public:
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
protected:
	Singleton() {}
	virtual ~Singleton() {}
public:
	static T& instance()
	{
		static T theInstance;
		return theInstance;
	}
};

class MyClass : public Singleton < MyClass>
{
	int x;
protected:
	friend class Singleton<MyClass>;
	MyClass() { x = 0; }
public:
	void setValue(int n) { x = n; }
	int getValue() const { return x; }
};

int main()
{
	MyClass& m = Singleton<MyClass>::instance();
	//MyClass& m = MyClass::instance(); 
	std::cout << m.getValue() << std::endl;
	m.setValue(1);
	std::cout << m.getValue() << std::endl;
	return 0;
}

问题:如何实现MyClass的拷贝构造,赋值语句等。


使用线程函数 thread_once 设计线程安全的单例模式

https://vlambda.com/wz_x3M25HKl72.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值