• 美国发起贸易战,我们要让世界知道美元、美债并不可靠 2019-06-05
  • 紫光阁中共中央国家机关工作委员会 2019-05-31
  • 监察体制改革后 湘西半年72名公职人员主动交代问题 2019-05-12
  • 媒体宣传报道重庆日报 王国平:扮靓重庆两江四岸” 让城市有机更新 2019-04-26
  • 我相信“交警雨中护送高考生”是真,“交警雨中护送高考生”反被该高考生家长投诉是假。 2019-04-16
  • 14名消防员日巡逻28公里 洗冷水澡 2019-04-10
  • 靶壕有了“蓝军”,百发百中的“神枪手”练起来 2019-04-10
  • 不是秀强大了,别人就会来做朋友,这逻辑不对 2019-04-01
  • 候选企业:中国石油呼和浩特石化公司 2019-03-26
  • 航天员沙漠野外生存训练完美收官!为第一天团打call 2019-03-25
  • 请问,建立市场经济后,原计划经济哪里去?改革后,我们还在实行计划经济,为何没有提及? 2019-03-25
  • 构建年轻干部梯次培养链 2019-03-19
  • 孙实的专栏作者中国国家地理网 2019-03-15
  • 湖南师范大学举行研究阐释党的十九大精神国家社科基金重大专项学术研讨会 2019-03-15
  • [雷人]蠢货!土地处于不同的城市和地段,关联的资源不一样,价值也不一样。不然给咱俩同样面积的土地,咱的在北上广深,你的在边远山区,你干么? 2019-03-08
  • 频道栏目
    神奇公式秒杀全国11选5 > 程序开发 > 软件开发 > C++ > 正文
    C++的单例模式的几种实现方式解析
    2018-07-27 13:51:52         来源:_____________________________________simanstar____  
    收藏   我要投稿

    神奇公式秒杀全国11选5 www.2zfa.com 单例模式有两种实现模式:

    1)懒汉模式: 就是说当你第一次使用时才创建一个唯一的实例对象,从而实现延迟加载的效果。

    2)饿汉模式: 就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

    所以,从实现手法上看,懒汉模式是在第一次使用单例对象时才完成初始化工作。因为此时可能存在多线程竞态环境,如不加锁限制会导致重复构造或构造不完全问题。

    饿汉模式则是利用外部变量,在进入程序入口函数之前就完成单例对象的初始化工作,此时是单线程所以不会存在多线程的竞态环境,故而无需加锁。

    以下是典型的几种实现

    一、 懒汉模式,标准的 ”双检锁“ + ”自动回收“ 实现

    class Singleton
    {
    public:
        static Singleton* GetInstance()
        {
            if (m_pInstance == NULL )
            {
                Lock(); // 加锁
                if (m_pInstance == NULL )
                {
                    m_pInstance = new Singleton ();
                }
                UnLock(); // 解锁
            }
            return m_pInstance;
        }
    
        // 实现一个内嵌垃圾回收类    
        class CGarbo 
        {
        public:
            ~CGarbo()
            {
                if(Singleton::m_pInstance) 
                    delete Singleton::m_pInstance;
            }
        };
    
        static CGarbo Garbo; // 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
    
    private:
        Singleton(){};
        Singleton(Singleton const&); 
        Singleton& operator=(Singleton const&); 
    
        static Singleton* m_pInstance;
    };
    
    Singleton* Singleton::m_pInstance = NULL;
    Singleton::CGarbo Garbo;

    二、静态局部变量的懒汉模式 ,而不是new在堆上创建对象,避免自己回收资源。

    这里仍然要注意的是局部变量初始化的线程安全性问题,在C++0X以后,要求编译器保证静态变量初始化的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。

    class Singleton
    {
    public:
        static Singleton* GetInstance()
        {
            Lock(); // not needed after C++0x 
            static Singleton instance;  
            UnLock(); // not needed after C++0x 
    
            return &instance;
        }
    
    private:
        Singleton() {};
        Singleton(const Singleton &);
        Singleton & operator = (const Singleton &);
    };

    在懒汉模式里,如果大量并发线程获取单例对象,在进行频繁加锁解锁操作时,必然导致效率低下。

    三、饿汉模式,基础版本

    因为程序一开始就完成了单例对象的初始化,所以后续不再需要考虑多线程安全性问题,就可以避免懒汉模式里频繁加锁解锁带来的开销。

    class Singleton
    {
    public:
    
        static Singleton* GetInstance()
        {
            return &m_instance;
        }
    
    private:
        Singleton(){};
        Singleton(Singleton const&); 
        Singleton& operator=(Singleton const&); 
    
        static Singleton m_instance;
    };
    
    Singleton Singleton::m_instance;  // 在程序入口之前就完成单例对象的初始化

    虽然这种实现在一定程度下能良好工作,但是在某些情况下会带来问题 --- 就是在C++中 ”非局部静态对象“ 的 ”初始化“ 顺序 的 ”不确定性“, 参见Effective c++ 条款47。

    考虑: 如果有两个这样的单例类,将分别生成单例对象A, 单例对象B. 它们分别定义在不同的编译单元(cpp中), 而A的初始化依赖于B 【 即A的构造函数中要调用B::GetInstance() ,而此时B::m_instance 可能还未初始化,显然调用结果就是非法的 】, 所以说只有B在A之前完成初始化程序才能正确运行,而这种跨编译单元的初始化顺序编译器是无法保证的。

    四、饿汉模式,增强版本(boost实现)

    在前面的方案中:饿汉模式中,使用到了类静态成员变量,但是遇到了初始化顺序的问题; 懒汉模式中,使用到了静态局部变量,但是存在着线程安全等问题。

    boost 的实现方式是:单例对象作为静态局部变量,然后增加一个辅助类,并声明一个该辅助类的类静态成员变量,在该辅助类的构造函数中,初始化单例对象。以下为代码

    class Singleton
    {
    public:
        static Singleton* GetInstance()
        {
            static Singleton instance;
            return &instance;
        }
    
    protected:
        // 辅助代理类
        struct Object_Creator
        {
            Object_Creator()
            {
                Singleton::GetInstance();
            }
        };
        static Object_Creator _object_creator;
    
        Singleton() {}
        ~Singleton() {}
    };
    
    Singleton::Object_Creator Singleton::_object_creator; 

    首先,代理类这个外部变量初始化时,在其构造函数内部调用Singleton::GetInstance();从而间接完成单例对象的初始化,这就通过该代理类实现了饿汉模式的特性。

    其次,仍然考虑第三种模式的缺陷。 当A的初始化依赖于B,【 即A的构造函数中要调用B::GetInstance() ,而此时B::m_instance 可能还未初始化,显然调用结果就是非法的 】 现在就变为【在A的构造函数中要调用B::GetInstance() ,如果B尚未初始化,就会引发B的初始化】,所以在不同编译单元内全局变量的初始化顺序不定的问题就随之解决。

    最后,关于使用懒汉还是饿汉模式,我的理解:

    如果这个单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,也是一种资源浪费吧。 所以这种情况懒汉模式(延迟加载)更好。

    如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

    点击复制链接 与好友分享!回本站首页
    上一篇:C++编程之引用和拷贝构造函数、按值传递和返回、位拷贝与初始化等实例
    下一篇:最后一页
    相关文章
    图文推荐
    点击排行

    关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 神奇公式秒杀全国11选5

    版权所有: 神奇公式秒杀全国11选5--致力于做实用的IT技术学习网站

  • 美国发起贸易战,我们要让世界知道美元、美债并不可靠 2019-06-05
  • 紫光阁中共中央国家机关工作委员会 2019-05-31
  • 监察体制改革后 湘西半年72名公职人员主动交代问题 2019-05-12
  • 媒体宣传报道重庆日报 王国平:扮靓重庆两江四岸” 让城市有机更新 2019-04-26
  • 我相信“交警雨中护送高考生”是真,“交警雨中护送高考生”反被该高考生家长投诉是假。 2019-04-16
  • 14名消防员日巡逻28公里 洗冷水澡 2019-04-10
  • 靶壕有了“蓝军”,百发百中的“神枪手”练起来 2019-04-10
  • 不是秀强大了,别人就会来做朋友,这逻辑不对 2019-04-01
  • 候选企业:中国石油呼和浩特石化公司 2019-03-26
  • 航天员沙漠野外生存训练完美收官!为第一天团打call 2019-03-25
  • 请问,建立市场经济后,原计划经济哪里去?改革后,我们还在实行计划经济,为何没有提及? 2019-03-25
  • 构建年轻干部梯次培养链 2019-03-19
  • 孙实的专栏作者中国国家地理网 2019-03-15
  • 湖南师范大学举行研究阐释党的十九大精神国家社科基金重大专项学术研讨会 2019-03-15
  • [雷人]蠢货!土地处于不同的城市和地段,关联的资源不一样,价值也不一样。不然给咱俩同样面积的土地,咱的在北上广深,你的在边远山区,你干么? 2019-03-08