设计模式笔记-单例模式

来源:互联网 发布:淘宝模特小茹 编辑:程序博客网 时间:2024/06/17 21:27

单例模式在系统开发时很常见,一个系统中可能会有多个单例模式的运用!它比全局变量的好处?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-functionstemplate<typename T>struct has_no_destroy{template <typename C> static char test(decltype(&C::no_destroy)); // or decltype in C++11template <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));}





0 0