单例模式简介

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式简介相关的知识,希望对你有一定的参考价值。

什么是设计模式

设计模式代表了最佳实践,是软件开发过程中面临一般问题的解决方案。

设计模式是一套被反复使用、经过分类、代码设计总结的经验。

单例模式

单例模式也叫单件模式。Singleton是一个非常常用的设计模式,几乎所有稍微大一些的程序都会使用到它,所以构建一个线程安全并且高效的Singleton很重要。

1. 单例类保证全局只有一个唯一实例对象。

2. 单例类提供获取这个唯一实例的接口。

怎样设计一个单例模式


实现一(不考虑线程安全)

class Singleton
{
public:
// 获取唯一对象实例的接口函数
	static Singleton* GetInstance()
	{
		if(_sInstance == NUL)
		{
			if (_sInstance == NULL)
			{
				_sInstance = new Singleton();
			}
		}
		return _sInstance;
	}
// 删除实例对象
	static void DelInstance()
	{
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
		cout<<_data<<endl;
	}
private:
	// 构造函数定义为私有,限制只能在类内创建对象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
	static Singleton* _sInstance;
	// 单例类里面的数据
	int _data;
};
Singleton* Singleton::_sInstance = NULL;

void TestSingleton()
{
	Singleton::GetInstance()->Print();
	Singleton::DelInstance();
}

实现二

线程安全的单例 -- (懒汉模式-- lazy loading)

ps: 下面部分的加锁使用了C++11库的互斥锁
class Singleton
{
public:
	// 获取唯一对象实例的接口函数
	static Singleton* GetInstance()
	{
	// 使用双重检查,提高效率,避免高并发场景下每次获取实例对象都进行加锁
		if (_sInstance == NULL)
		{
			std::lock_guard<std::mutex> lck(_mtx);
			if (_sInstance == NULL)
			{
				// tmp = new Singleton()分为以下三个部分
				// 1.分配空间 2.调用构造函数 3.赋值
				// 编译器编译优化可能会把2和3进行指令重排,这样可能会导致
				// 高并发场景下,其他线程获取到未调用构造函数初始化的对象
				// 以下加入内存栅栏进行处理,防止编译器重排栅栏后面的赋值
				// 到内存栅栏之前
				Singleton* tmp = new Singleton();
				MemoryBarrier();
				_sInstance = tmp;
			}
		}
		return _sInstance;
	}
	// 删除实例对象
	static void DelInstance()
	{
		std::lock_guard<std::mutex> lck(_mtx);
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
	cout<<_data<<endl;
	}
private:
	// 构造函数定义为私有,限制只能在类内创建对象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
	static Singleton* _sInstance;
	// 保证线程安全的互斥锁
	static mutex _mtx;
	// 单例类里面的数据
	int _data;
};
Singleton* Singleton::_sInstance = NULL;
mutex Singleton::_mtx;

void TestSingleton()
{
Singleton::GetInstance()->Print();
Singleton::DelInstance();
}

实现三

线程安全的单例 -- (饿汉模式--简洁、高效、不用加锁、但是在某些场景下会有缺陷)

方法1

// 方式一
class Singleton
{
public:
	// 获取唯一对象实例的接口函数
	static Singleton* GetInstance()
	{
		static Singleton sInstance;
		return &sInstance;
	}
	void Print()
	{
		cout<<_data<<endl;
	}
private:
	// 构造函数定义为私有,限制只能在类内创建对象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 单例类里面的数据
	int _data;
};

void TestSingleton()
{
	Singleton::GetInstance()->Print();
}

方法2

// 方式二
class Singleton
{
public:
	// 获取唯一对象实例的接口函数
	static Singleton* GetInstance()
	{
		assert(_sInstance);
		return _sInstance;
	}
	// 删除实例对象
	static void DelInstance()
	{
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
		cout << _data << endl;
	}
private:
	// 构造函数定义为私有,限制只能在类内创建对象
	Singleton()
	:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	// 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
	static Singleton* _sInstance;
	// 单例类里面的数据
	int _data;
};
Singleton* Singleton::_sInstance = new Singleton;

void TestSingleton()
{
	Singleton::GetInstance()->Print();
	Singleton::DelInstance();
}

带RAII GC 自动回收实例对象的方式

class Singleton
{
public:
	// 获取唯一对象实例的接口函数
	static Singleton* GetInstance()
	{
		assert(_sInstance);
		return _sInstance;
	}
	// 删除实例对象
	static void DelInstance()
	{
		if (_sInstance)
		{
			delete _sInstance;
			_sInstance = NULL;
		}
	}
	void Print()
	{
		cout << _data << endl;
	}
	class GC
	{
		public:
		~GC()
		{
			cout << "DelInstance()"<<endl;
			DelInstance();
		}
	};
private:
	// 构造函数定义为私有,限制只能在类内创建对象
	Singleton()
	:_data(0)
	{}
	// 指向实例的指针定义为静态私有,这样定义静态成员函数获取对象实例
	static Singleton* _sInstance;
	// 单例类里面的数据
	int _data;
};
// 静态对象在main函数之前初始化,这时只有主线程运行,所以是线程安全的。
Singleton* Singleton::_sInstance = new Singleton;
// 使用RAII,定义全局的GC对象释放对象实例
Singleton::GC gc;
void TestSingleton()
{
	Singleton::GetInstance()->Print();
}

由于程序在结束的时候,系统会自动析构所有的全局变量,实际上,系统也会析构所有类的静态成员变量,就像这些静态变量是全局变量一样。我们知道,静态变量和全局变量在内存中,都是存储在静态存储区的,所以在析构时,是同等对待的。


技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享技术分享

本文出自 “11408774” 博客,请务必保留此出处http://11418774.blog.51cto.com/11408774/1845595

以上是关于单例模式简介的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

常用代码片段

[01]Go设计模式:单例模式(Singleton)

C++之单例(singleton)模式

JAVA设计模式—单例模式

JAVA设计模式—单例模式