用c++11做单例模式

    做个工程上能用的单例模式,不仅要支持多线程,也要支持多参数传递,也要支持饿汉和懒汉两种模式,用了下c++11的call_once, lamda表达式,可变参数模板和forward的转发,代码如下:

#include <iostream>
#include <mutex>
using namespace std;
template<class T>
class Singleton
{
public:
	template<class... Params>
	static void init(Params&&... params)
	{
		if(ins == NULL)
			ins=new T(std::forward<Params>(params)...);
	}
	static T* get()
	{
		if(ins == NULL)
		{
			cout<<"please init first."<<endl;
			return ins;
		}
		return ins;
	}

	template<class... Params>
	static T* getAndInit(Params&&... params)
	{
		static std::once_flag oc;//用于call_once的局部静态变量
   		std::call_once(oc, [&]{ins = new T(std::forward<Params>(params)...);});
    	        return ins;
	}
	static void destroy()
	{
		delete ins;
		ins=NULL;
	}
private:
	Singleton();
	virtual ~Singleton();
	Singleton(const Singleton& s);
	Singleton& operator=(const Singleton& s);
	static T* ins;
};
template<class T> T* Singleton<T>::ins=NULL;

其中init和get分离的是饿汉模式,在多线程程序中,潜在出现竞争的是new的地方,将init放到一个安全的地方,比如主进程的初始化部分,可以避免竞争。

getAndInit是典型的懒汉模式,即在get的时候检查初始化,传统的处理方法是double check+内存栅栏,不过既然c++11提供了call_once这么适合单例的操作,不用就可惜了,call_once的实际工作用的是lamda表达式。

template<class... Params> 比传统的template多了3个点,实现了可变参数模板,forward直接把可变的参数完美转发了。

Singleton的private部分是为了禁止构造,析构,拷贝构造和赋值,多说一句virtual修饰析构,是为了调用派生类和基类的析构函数,应对了动态绑定,预防了内存泄漏。

在函数外的ins赋值为NULL的部分,完成对ins的定义,这里需要在template之后明确的指定类型为T*。

附一个测试例子,注释掉的部分是测试饿汉的部分。

struct A
{
	int a;
	A(int val)
	{
		a=val;
		cout<<"A is construct"<<endl;
	}
};
int main()
{
	// Singleton<A>::init(21);
	// A* test1=Singleton<A>::get();
	// cout<<test1<<endl;
	// A* test2=Singleton<A>::get();
	A* test1=Singleton<A>::getAndInit(32);
	A* test2=Singleton<A>::getAndInit(34);
	cout<<test1<<endl;
	cout<<test2<<endl;
	cout<<test2->a<<endl;
	Singleton<A>::destroy();
	return 0;
}

编译的方法是:

g++ test.cc -o test -std=c++11 -pthread
注意用到了call_once这个函数,所以编译的时候需要pthread。

猜你喜欢

转载自blog.csdn.net/dong_beijing/article/details/80955461