zoukankan      html  css  js  c++  java
  • 模板单例实现

    #模板单例实现
    本文参考 [用模板实现单例模式](http://blog.csdn.net/jnu_simba/article/details/9398465 "用模板实现单例模式") ,并且在此基础上做了适当的改进,使其支持更多功能。
        #ifndef Singleton_h__
        #define Singleton_h__
        template<class T>
        class Singletion
        {
        public:
        static T* GetInstance()
        {

            static T m_Instance;

            return &m_Instance;
        }

        private:
            Singletion(){};
            virtual ~Singletion(){};
            Singletion(const Singletion& ){};
            Singletion& operator=(const Singletion&){};
        };
        endif // Singleton_h__
        /*
    具体使用方法如下:
        // 定义具体单例
           class CCenterImp
           {   
                public: // 构造函数要为public,不能为private和protect
                    CCenterImp();     // 在构造函数中进行必要的初始化 
                    ~CCenterImp ();  // 在析构函数中进行资源的释放
                private:
                   // Method
                    void print();
                   // Variable
                }
            }
           typedef Singletion<CCenterImp> CCenter;
          // 使用单例方法
          CCenter::GetInstance()->print();
    注意,要以typedef的方法来定义单例具体名称,具体单例CCenterImp的构造函数中初始化相关资源,在析构函数中释放相关资源。
    提出问题1:为什么不用双重double的方式?

    回答:双重double检查外加互斥锁的方式可以在满足获取要求,但什么时候释放申请的内存不好控制,以下是改进版双重检测

            if (NULL == m_pInstance)

       {

        // Lock相关

        if (NULL == m_pInstance)

        {

          T* temp = new T();  // 先new一个示例出来赋给临时变量

          m_pInstance = temp;  // 在赋值给真实的变量

        }

       }

      return m_pInstance;

        如果不需要做成模板复用的话,可以在此单例子中在增加一个内部嵌套静态类,在其析构函数中释放这里申请的内存,但如果要做成模板类的话,这个内嵌类就没法实现了,因此放弃这种方式。

    思考2:对于new出一个单例的方式,如何放置释放处理函数。

    回答: 参考网上的处理,有atexit函数,此函数是<stdlib.h>中的函数,可以注册一系列函数,用于在main函数退出后执行,在单例实例不多的情况下,可以考虑使用,但一定要注意它自身的限制,ISO C规定,一个进程在退出时,最多可以注册32个atexit函数,这些函数依次被exit函数所调用,如果要做成模板单例,供其他模块使用,就会有数量上的限制。 

    思考3:对于new出来的实例,尝试保存它自身以及相关析构函数,在系统销毁时,统一调用一次。

    回答:今天曾尝试着将new出来的实例地址以及它对应的析构函数地址存放在map中,存放在另一个单例中,等系统快要退出时统一显示调用调用。这里有2个问题,一个是多个不同的单例实例,只能以void*的形式保存其指针,在准备调用delete时,指针保存的类信息丢失了,无法调用到其析构函数。 网上有利用汇编技巧的方法获取析构函数的地址,参考链接:http://www.cnblogs.com/findumars/p/3746869.html, 我尝试了下,执行到析构函数时发生崩溃了,尝试了多种方式,还是不行,此路不通。

    思考4:单例类可以在外部new一个出来或者在本地定义一个局部变量吗?

    回答:因为要构造一个单例类,所以这个单例类的构造函数一定不能是private或者是protect类型的,但是,这样一类,就可以直接在外部定义这样一个类了,不管你是在单例的GetInstance里面new出来,还是 局部静态变量出来,都需要构造函数。

    思考5: 局部静态变量的单例模式需不需要加锁?

    回答: 参考此篇文章,http://blog.csdn.net/yichigo/article/details/37878117, 如果加锁保护,那么以后每次调用GetInstance都会有锁,如果不加锁,如果构造函数中执行的时间较长,在多线程环境下,可能会这个变量构造到一半,就被另一个线程拿过去用了。为了平衡这两种情况,在程序中,对于此单例有可能有多线程访问的情况下,在程序启动初始化时,显示调用一个CCenter::GetInstance(),手动触发初始化操作后,然后开启多线程,这样,既保证了在多线程工作时,此单例肯定是初始化完成了,在获取时,也不需要加锁,快速直接。备注:局部静态变量的线程安全性,在C++0x上的编译器可以保证,在linux平台下的gcc编译器可以保证,在低版本的VC下不保证,因此,不要把这个变化的部分推给编译器。在程序初始化时,显示调用一次,确保构造完成后,再开启多线程相关工作,我们是可以掌控这种情况的。

    思考6: 对于上文提出的,静态局部变量的方式,有一点要注意,模板类里面的构造、析构、拷贝构造、赋值要设置private,实现类里面的构造要设置为public,不能设置为protect和private。这里有一个小缺陷,用户可以定义或者new CCenterImp 类,但不能定义或者new CCenter类。整个单例对外表现的对象为CCenter类,但是,CCenterImp只能定义在头文件里面,这样,别人看到了就有误用的可能,想来想去,也没想到什么好的方法,只能通过注释文字说明来提示使用者,使用CCenter类,而不要直接使用CenterImp类。

        好了,关于单例的思考和讨论汇总为这么多,以后在使用时,如果需要复用,就用上述提到的模板类,如果不需要复用,简简单单一个局部静态变量加上手动初始化,可确保万事大吉。

  • 相关阅读:
    git 备查
    PyQt5 信号与槽机制
    Python 循环报错 'int' object is not iterable
    docker instructions命令及dockerfile实践
    shell脚本中set指令
    你知道什么是Python算法和数据结构、抽象数据和面向对象、数组和列表、链表吗?
    Python爬虫抓取B站人类高质量男性求偶视频的弹幕,康康都在讨论什么!❤️
    Python从入门到精通要掌握哪些基础知识?
    想搞个恶作剧吗? 代码丢给他,生死有命富贵在天,看我学会python用代码整蛊朋友
    妹妹画的小恐龙和我用Python画的小恐龙,你更加喜欢谁的!❤️
  • 原文地址:https://www.cnblogs.com/cherishui/p/6970481.html
Copyright © 2011-2022 走看看