美文网首页
单例模式

单例模式

作者: analanxingde | 来源:发表于2018-05-03 09:03 被阅读15次

    定义

    单例模式(Singleton Pattern,也称为单件模式),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

    定义一个单例类,私有化它的构造函数,以防止外界创建单例类的对象;使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。

    单例模式类图

    代码

    懒汉模式

    //头文件  
    class Singleton  
    {  
        public:  
            static Singleton& Instance()                  //Instance()作为静态成员函数提供里全局访问点  
            {  
                if(ps == NULL)                        //如果还未实例化,即可实例话,反之提供实例的引用  
                    ps = new Singleton;  
                return *ps;                           //返回指针的话可能会误被 delete,返回引用安全一点  
            }  
      
        private:  
            Singleton();                                  //这里将构造,析构,拷贝构造,赋值函数设为私有,杜绝了生成新例  
            ~Singleton();  
            Singleton(const Singleton&);  
            Singleton& operator=(const Singleton&);  
      
            static Singleton* ps;  
    };  
      
    //源文件  
    Singleton* Singleton::ps = NULL;
    

    这种方法的好处在于直到 Instance() 被访问,才会生成实例,这种特性被称为延迟初始化(Lazy Initialization),这在一些初始化时消耗较大的情况有很大优势。
    Lazy Singleton不是线程安全的,比如现在有线程A和线程B,都通过了 ps == NULL 的判断,那么线程A和B都会创建新实例。单例模式保证生成唯一实例的规则被打破了。
    改进的线程安全的懒汉模式
    可以通过加锁来保护单例初始化这一过程,双检测锁模式就是在懒汉模式的基础上稍作修改得到:

    //头文件  
    class Singleton  
    {  
        public:  
            static Singleton& Instance()                //Instance()作为静态成员函数提供里全局访问点  
            {  
                if(ps == NULL)  
                {     
                    Lock();             //上锁  
                    if(ps == NULL)          //如果还未实例化,即可实例话,反之提供实例的引用  
                        ps = new Singleton;  
                    Unlock();           //解锁  
                }  
                return *ps;                             //返回指针的话可能会误被 delete,返回引用安全一点  
            }     
      
        private:  
            Singleton();                                    //这里将构造,析构,拷贝构造,赋值函数设为私有,杜绝了生成新例  
            ~Singleton();  
            Singleton(const Singleton&);  
            Singleton& operator=(const Singleton&);  
      
            static Singleton* ps;  
    };  
      
    //源文件  
    Singleton* Singleton::ps = NULL;  
    

    上锁和解锁仅用于说明,实际应用中可以使用互斥锁,单一信号量等方法去实现。理论上问题解决了,但是在实践中有很多坑,如指令重排、多核处理器等问题让DCLP实现起来比较复杂比如需要使用内存屏障

    饿汉模式

    //头文件  
    class Singleton  
    {  
        public:  
            static Singleton& Instance()                  //Instance()作为静态成员函数提供里全局访问点  
            {  
                return instance;  
            }  
      
        private:  
            Singleton();                                  //这里将构造,析构,拷贝构造,赋值函数设为私有,杜绝了生成新例  
            ~Singleton();  
            Singleton(const Singleton&);  
            Singleton& operator=(const Singleton&);  
      
            static Singleton instance;  
    };  
      
    //源文件  
    Singleton Singleton::instance; 
    

    与懒汉模式相反,实例化是在初始化阶段执行的,所以没有线程安全的问题,但是潜在问题在于no-local static对象(函数外的static对象)在不同编译单元(可理解为cpp文件和其包含的头文件)中的初始化顺序是未定义的。如果在初始化完成之前调用 Instance()方法会返回一个未定义的实例。

    优雅的单例模式实现

    //头文件  
    class Singleton  
    {  
        public:  
            static Singleton& Instance()                  //Instance()作为静态成员函数提供里全局访问点  
            {  
                static Singleton instance;  
                return instance;  
            }  
      
        private:  
            Singleton();                                  //这里将构造,析构,拷贝构造,赋值函数设为私有,杜绝了生成新例  
            ~Singleton();  
            Singleton(const Singleton&);  
            Singleton& operator=(const Singleton&);       
    }; 
    

    使用local static对象(函数内的static对象)。当第一次访问 Instance() 方法时才创建实例。该实现是线程安全的

    借助pthread_once() 函数实现单例模式

    在多线程编程环境下,尽管 pthread_once() 调用会出现在多个线程中,init_routine()函数仅执行一次,pthread_once是很适合用来实现线程安全单例。(pthread_once 在一个进程里只会执行一次,其实现方式使用的就是互斥锁+条件变量的方法)

    //头文件  
    pthread_once_t once = PTHREAD_ONCE_INIT;  
    class Singleton  
    {  
        public:  
            static Singleton& Instance()                  //Instance()作为静态成员函数提供一次实例化以及全局访问点  
            {  
                pthread_once(&once, &Init);  
                return *ps;  
            }  
      
            static void Init()  
            {  
                ps = new Singleton;  
            }  
      
        private:  
            Singleton();  
            ~Singleton();  
            Singleton(const Singleton&);  
            Singleton& operator=(const Singleton&);  
      
            static Singleton* ps;  
    };  
    //源文件  
    Singleton* Singleton::ps = NULL;  
    

    单例模式的实现方法很多,要完成一个完美的实现很难,代码也会很复杂,但是掌握基础的实现还是很必要的,文中提到的优雅的单例模式借助pthread_once() 函数实现单例模式是相对比较完善的方法。

    相关文章

      网友评论

          本文标题:单例模式

          本文链接:https://www.haomeiwen.com/subject/woncrftx.html