设计模式笔记-单例模式

单例模式在系统开发时很常见,一个系统中可能会有多个单例模式的运用!它比全局变量的好处?1.能控制生成时间,保证初始化顺序?2. 容易保证线程安全(全局变量需要多线程client单独控制并发,单例在类实现中已经做到,无需client再处理)?3. 减少函数模块之间的耦合,减少命名冲突,便于管理? 可能这些都是好处吧!

我的项目开发过程中,经常会把单例模式和简单工厂模式结合起来使用!比如:

class OPTSOLVER_API CGasElementFactory
{
public:

	~CGasElementFactory();

	static CGasElementFactory& GetInstance();
	virtual LPVOID CreateElement( IN DWORD nID, IN tstring strName, IN PIPELINE_ELEMENT_TYPE emType );

protected:

	CGasElementFactory();
	CGasElementFactory(const CGasElementFactory&);
	CGasElementFactory & operator = (const CGasElementFactory &);

};

比较重要的就是其中的GetInnstance函数了,这的实现是:

CGasElementFactory& CGasElementFactory::GetInstance()
{
	static CGasElementFactory s_modelValidatorFactory;

	return s_modelValidatorFactory;
}
也就是说现在一般都不使用那种静态指针变量的方式了。可能是考虑到资源的释放吧,不过我觉得即使开发人员在系统的最后忘了释放单例new的对象,在系统运行结束时操作系统会自动收回!反正都是运行期一直存在的。采用上面的方式可能看着更优雅!

可能有人注意到上面代码中有一个受保护的=操作重载,这是因为,如果有人写出如下的代码:

CGasElementFactory factory = CGasElementFactory::GetInstance();
单例可以拷贝的话,就违背了单例的意义!因此要把它设为受保护成员!拷贝构造也要保护,虽然这样使用的不多。至此,一个单例类基本就完成了, 具体到使用上就是CGasElementFactory::GetInstance().xxx(这里是CreateElement函数)()!!!

至于线程安全的,我直接拷贝网上别人的代码吧!

class Lock
{
private:      
    mutex mtex;
public:
    Lock(mutex m) : mtex(m)
    {
        mtex.Lock();
    }
    ~Lock()
    {
        mtex.Unlock();
    }
};
class Singleton {
public:
    static Singleton* getInstance();
    //析构的时候释放资源~
    ~Singleton() {
        if( (_instance != NULL)
            delete _instance;
    }
protected:
    Singleton();
private:
    static Singleton* _instance;
    static mutex m;
}
 
Singleton *Singleton::_instance = NULL;
Singleton* Singleton::getInstance() {
    //check 之前进行临界区加锁操作
    //双重检验加锁
    if(_instance == NULL ) {
        Lock lock(m);
        if( _instance == NULL) {
            _instance = new Singleton();
        }
    }
    return _instance;
}

因为加锁比较耗时,又因为_instance只在第一次使用(为NULL)的时候才会跑到锁里面,所以外层的if判断就阻止了每次都加锁的操作!

下面的单例代码参考muduo库用C++11里的线程函数实现。这里用到C++模板的SFINAE特性,singleton在程序结束时用自带的destroy()函数释放资源。其实费了这么大劲,也就是为了在singleton析构时能释放掉资源。但singleton一般都是从创建开始一直存在到程序运行结束的,程序结束时它申请的所有资源都被操作系统回收。后面的测试程序显示有内存泄露,不太理解,虽然从表面上看TestNoDestroy类析构是没有调用。

#include <thread>
#include <mutex>
#include <fstream>
#include <cassert>
using namespace std;

namespace detail
{
	// This doesn't detect inherited member functions!
	// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions
	template<typename T>
	struct has_no_destroy
	{
		template <typename C> static char test(decltype(&C::no_destroy)); // or decltype in C++11
		template <typename C> static int32_t test(...);
		const static bool value = sizeof(test<T>(0)) == 1;
	};
}
template<typename T>
class Singleton
{
public:
	static T& instance()
	{
		call_once(ponce_, Singleton::init);
		assert(value_ != NULL);
		return *value_;
	}

private:
	Singleton();
	~Singleton();

	static void init()
	{
		value_ = new T();
		if (!detail::has_no_destroy<T>::value)
		{
			::atexit(destroy);
		}
	}

	static void destroy()
	{
		typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
		T_must_be_complete_type dummy; (void)dummy;

		delete value_;
		value_ = NULL;
	}

private:
	static std::once_flag ponce_;
	static T*             value_;
};

template<typename T>
once_flag Singleton<T>::ponce_;

template<typename T>
T* Singleton<T>::value_ = NULL;

下面是测试程序:

class Test
{
public:
	Test()
	{
		printf("tid=%d, constructing %p\n", this_thread::get_id(), this);
	}

	~Test()
	{
		printf("tid=%d, destructing %p %s\n", this_thread::get_id(), this, name_.c_str());
	}

	const string& name() const { return name_; }
	void setName(const string& n) { name_ = n; }

private:
	string name_;
};

class TestNoDestroy
{
public:
	// Tag member for Singleton<T>
	void no_destroy();

	TestNoDestroy()
	{
		printf("tid=%d, constructing TestNoDestroy %p\n", this_thread::get_id(), this);
	}

	~TestNoDestroy()
	{
		printf("tid=%d, destructing TestNoDestroy %p\n", this_thread::get_id(), this);
	}
};

void threadFunc()
{
	printf("tid=%d, %p name=%s\n",
		this_thread::get_id(),
		&Singleton<Test>::instance(),
		Singleton<Test>::instance().name().c_str());
	Singleton<Test>::instance().setName("only one, changed");
}

int main()
{
	Singleton<Test>::instance().setName("only one");
	thread t1(threadFunc);
	t1.join();
	printf("tid=%d, %p name=%s\n",
		this_thread::get_id(),
		&Singleton<Test>::instance(),
		Singleton<Test>::instance().name().c_str());
	Singleton<TestNoDestroy>::instance();
	printf("with valgrind, you should see %zd-byte memory leak.\n", sizeof(TestNoDestroy));
	
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值