单例

来源:互联网 发布:python db2 连接 编辑:程序博客网 时间:2024/04/27 03:37

以下观点都不是绝对的,都是相对的,谢谢

 

(1)    全局变量的缺点

a.       可扩展性较差

b.       维护性较差,在大型工程中可能存在多人同时开发,谁对全局变量进行了什么操作,无从得知

(2)    何为单例模式?

单例模式是24种设计模式中比较常用的一种,顾名思义就是保证某个类的实例只有一个。

 

(3)    单例模式实现的问题

一般来说单例模式的实现存在如下问题:

a.       如何保证外部不会生成这个类的多个实例

类的实例在一个进程中不外乎4种存储形式,全局变量, static变量,堆变量,栈变量。都是通过调用构造函数的形式来实现变量的内存空间分配和初始化,所以只要禁止外界对构造函数的调用,即可达到该目的。很明显将构造函数声明为protected或者private即可达到该目的。同理,将拷贝构造函数和赋值函数也需要声明为protected或者private.这样当显式的去实现某个类的4种形式的变量是,就会得到编译错误。

 

Class Singleton

{

        Public:

               ~Singleton();

                     Protected:

                            Singleton();                                               //构造函数

                            Singleton(const Singleton&);                      //拷贝构造函数

                            Singleton& operator = (const Singleton&);   //赋值函数

              };

 

b.       如何获取单例模式的句柄

上一步我们已经初步实现了一个不能够被直接初始化的类:Singleton,

但是如何实现这个类的实例,并且获取这个唯一的句柄呢?

不要忘记C++的类方法和类变量(类中的static方法和static变量)

 

class Singleton

{

        public:

               ~Singleton()

               static Singleton* GetHandle()

               {

                   if (Singleton::sHandle == NULL)

                   {

                      Singleton::sHandle = new Singleton();

                   }

            }

        protected:

                      Singleton();                                                   //构造函数

                      Singleton(const Singleton&);                          //拷贝构造函数

                      Singleton& operator = (const Singleton&);       //赋值函数

        private:

                static Singleton* sHandle;

};

 

Singleton* Singleton::sHandle = NULL;

 

如上,我们便实现了一个根据类方法获取一个实例句柄,目前来看基本达到了我们前面的要求。

 

c.       如何保证线程安全

不要诧异,我们很多情况都是在多线程中运行的。

如下语句在多线程环境中会造成多个实例:

       static Singleton* GetHandle()

               {

                   if (Singleton::sHandle == NULL)

                   {

                      Singleton::sHandle = new Singleton();

                   }

            }

如果你确保第一次调用肯定不会有多个线程的情况,那么也不需要考虑这个问题。

但是不保证别人使用你的代码时,也考虑了这个问题

 

class Singleton

{

                public:

                        ~Singleton();

                        static Singleton* GetHandle()

                        {

                                if (!Singleton::sm_flag)

                                {

                                        pthread_mutex_lock(&sm_mutex);

                                        if (!Singleton::sm_flag)

                                        {

                                                Singleton::sHandle = new Singleton();

                                                sm_flag = true;

                                        }

                                        pthread_mutex_unlock(&sm_mutex);

                                }

                                                         return Singleton::sHandle;

                        }

                protected:

                        Singleton()

                        {

                                cout<<"Singleton()"<<endl;

                        }

                        Singleton(const Singleton&);

                        Singleton& operator = (const Singleton&);

                private:

                        static Singleton* sHandle;

                        static pthread_mutex_t sm_mutex;

                        static bool sm_flag;

};

 

 

Singleton* Singleton::sHandle = NULL;

pthread_mutex_t Singleton::sm_mutex = PTHREAD_MUTEX_INITIALIZER;

bool Singleton::sm_flag = false;

d.       何时析构

如果按照上面的实现,我们的单例模式是随进程消亡而消亡的,是看不到析构函数的调用的,这样做是否有问题?

当然有了, 如果单例中存放的是某种资源,比如数据库连接,socket, 进程间的通信方式等就比较麻烦了。

如何析构呢?

      

下面这个类巧妙利用static变量的自动析构属性来析构单例的句柄

#include <iostream>

#include <pthread.h>

using namespace std;

 

 

class Singleton

{

class InstanceHandler

{

        public:

               InstanceHandler() : m_Handler(NULL)

               {

               }

              

               ~InstanceHandler()

               {

                      if (NULL != m_Handler)

                      {

                             delete m_Handler;

                      }

               }

              

               Singleton* get() const

               {

                      return m_Handler;

               }

 

               void set(Singleton* p)

               {

                      this ->m_Handler = p;

               }

 

        protected:

               InstanceHandler(const InstanceHandler&);

               InstanceHandler& operator = (const InstanceHandler&);

              

        private:

               Singleton* m_Handler;

};

 

public:

 

        ~Singleton()

        {

               cout<<"~Singleton()"<<endl;

        }

 

        static Singleton* GetHandle()

        {

               if (!Singleton::sm_flag)

               {

                      pthread_mutex_lock(&sm_mutex);            

                      if (!Singleton::sm_flag)

                      {

                             sm_handler.set(new Singleton());

                             sm_flag = true;

                      }

                      pthread_mutex_unlock(&sm_mutex);  

               }

 

               return sm_handler.get();

        }

       

protected:

        Singleton()

        {

               cout<<"Singleton()"<<endl;

        }    

        Singleton(const Singleton&);

        Singleton& operator = (const Singleton&);

private:

        static Singleton* sHandle;

        static pthread_mutex_t sm_mutex;

        static bool sm_flag;

        static InstanceHandler sm_handler;

};

 

 

Singleton* Singleton::sHandle = NULL;

pthread_mutex_t Singleton::sm_mutex = PTHREAD_MUTEX_INITIALIZER;

bool Singleton::sm_flag = false;

Singleton::InstanceHandler Singleton::sm_handler;

 

int main()

{

{

        Singleton* pHandle = Singleton::GetHandl

    }

}

 

e.       析构的优先级

 

当系统中存在多个实例的时候,且某些实例依赖于其他实例,也就是说实例的析构必须要有先后顺序,这个时候上面的办法就行不通了。

 

这个时候我可以利用标准库中的atexit函数, 如下所示:

atexit函数具有后进先出的特点,刚好可以满足我们一定的析构顺序

 

#include <iostream>

#include <pthread.h>

#include <stdlib.h>

using namespace std;

 

 

class Singleton

{

        public:

               ~Singleton()

               {

                      cout<<"~Singleton()"<<endl;

               }

               static Singleton* GetHandle()

               {

                      if (!Singleton::sm_flag)

                      {

                             pthread_mutex_lock(&sm_mutex);            

                             if (!Singleton::sm_flag)

                             {

                                    Singleton::sHandle = new Singleton();

                                    sm_flag = true;

                             }

                              pthread_mutex_unlock(&sm_mutex);  

                      }

                    }

               static void Destroy()

               {

                      if (NULL != Singleton::sHandle)

                      {

                             delete Singleton::sHandle;             

                      }

               }

 

        protected:

               Singleton()

               {

                      cout<<"Singleton()"<<endl;

               }    

               Singleton(const Singleton&);

               Singleton& operator = (const Singleton&);

        private:

                static Singleton* sHandle;

               static pthread_mutex_t sm_mutex;

               static bool sm_flag;      

};

 

 

Singleton* Singleton::sHandle = NULL;

pthread_mutex_t Singleton::sm_mutex = PTHREAD_MUTEX_INITIALIZER;

bool Singleton::sm_flag = false;

 

int main()

{

 

atexit(Singleton::Destroy);

 

{

        Singleton* pHandle = Singleton::GetHandle();

}

}

 

f.        单例模式模板化

 

如果每个类都去这样写,岂不是很麻烦,这里提供1个模板的实现:

#include <iostream>

#include <pthread.h>

#include <stdlib.h>

using namespace std;

 

template <typename T>

class Singleton

{

        public:

               static T* GetHandle()

               {

                      if (!Singleton::sm_flag)

                      {

                             pthread_mutex_lock(&sm_mutex);            

                             if (!Singleton::sm_flag)

                             {

                                    Singleton::sHandle = new T();

                                    sm_flag = true;

                             }

                              pthread_mutex_unlock(&sm_mutex);  

                      }

                    }

               static void Destroy()

               {

                      if (NULL != Singleton::sHandle)

                      {

                             delete Singleton::sHandle;             

                      }

               }

 

        protected:

               Singleton();

               ~Singleton();

               Singleton(const Singleton&);

               Singleton& operator = (const Singleton&);

        private:

         static T* sHandle;

               static pthread_mutex_t sm_mutex;

               static bool sm_flag;      

};

 

template <typename T>

T* Singleton<T>::sHandle = NULL;

template <typename T>

pthread_mutex_t Singleton<T>::sm_mutex = PTHREAD_MUTEX_INITIALIZER;

template <typename T>

bool Singleton<T>::sm_flag = false;

 

class A

{

    friend class Singleton<A>;

 

     public:

~A()

{

        cout<<"~A()"<<endl;

}

 

     protected:

A()

{

        cout<<"A()"<<endl;

}

 

 

};

 

 

 

int main()

{

 

atexit(Singleton<A>::Destroy);

 

{

        A*  pHandle = Singleton<A>::GetHandle();

}

}

 

g. 这里有一点需要注意一下,为了防止别人可以直接delete 单例类的句柄,需要将每个放入模版中的

类的析构函数声明为protected, 这样就可以防止直接delete调用。

以上实现均是轻量级的“山寨”版本实现,优点是自己可以根据实际需要进行构造。

如果有想体验真正强大的单例模式模板应用,请参见Andrei Alexandrescu Loki

 

参考书籍 Andrei Alexandrescu << Modern C++ Design >>

                      侯捷翻译<<C++程序设计新思维>>