zoukankan      html  css  js  c++  java
  • BOOST的Singleton模版详解

    首先要说明,这个准确说并不是BOOST的singleton实现,而是BOOST的POOL库的singleton实现。BOOST库中其实有若干个singleton模版,这个只是其中一个。但网上大部分介绍的介绍的BOOST的Singleton实现都是这个,所以大家也就默认了。而且这个的确算是比较特殊和有趣的一个实现。

    网上比较有名的文章是这篇《2B程序员,普通程序员和文艺程序员的Singleton实现》 介绍,我虽然对Singleton模版无爱,但自己的项目组中也有人用这个实现,所以还是研究了一下这个实现,特别网上真正解释清楚这个东东的人并不多(包括原文),所以还是研究了一下。

    1          为啥2B实现有问题

    为了介绍清楚这个实现,我们还要先解释清楚为啥2B实现有问题,首先说明,2B实现和BOOST的实现都可以解决多线程调用Singleton导致多次初始化的问题。

     1 //H文件
     2 template <typename T> class Singleton_2B
     3 {
     4 protected:
     5     typedef  T  object_type;
     6     //利用的是类的静态全局变量
     7     static T instance_;
     8 public:
     9     static T* instance()
    10     {
    11         return &instance_;
    12     }
    13 };
    14 
    15 //因为是类的静态变量,必须有一个通用的声明
    16 template<typename T> typename Singleton_2B<T>::object_type  Singleton_2B<T>::instance_;
    17 
    18 //测试的例子代码。
    19 class Object_2B_1
    20 {
    21     
    22     //其实使用友元帮助我们可以让Object_2B的构造函数是protected的,从而真正实现单子的意图
    23     friend class Singleton_2B<Object_2B_1>;
    24     //注意下面用protected,大家无法构造实例
    25 protected:
    26     Object_2B_1();
    27     ~Object_2B_1(){};
    28 public:
    29     void do_something();
    30 protected:
    31     int data_2b_1_;
    32 };
    33 
    34 class Object_2B_2
    35 {
    36     friend class Singleton_2B<Object_2B_2>;
    37 protected:
    38     Object_2B_2();
    39     ~Object_2B_2(){};
    40 public:
    41     void do_something();
    42 protected:
    43     int data_2b_2_;
    44 };
    45 
    46 //CPP文件
    47 Object_2B_1::Object_2B_1():
    48     data_2b_1_(1)
    49 {
    50     printf("Object_2B_1::Object_2B_1() this:[%p] data_2b_1_ [%d].\n",this,data_2b_1_);
    51     Singleton_2B<Object_2B_2>::instance()->do_something();    
    52 };
    53 
    54 void Object_2B_1::do_something()
    55 {
    56     data_2b_1_+= 10000;
    57     printf("Object_2B_1::do_something() this:[%p] data_2b_1_ [%d].\n",this,data_2b_1_);
    58     
    59 }
    60 
    61 
    62 Object_2B_2::Object_2B_2():
    63         data_2b_2_(2)
    64 {
    65     printf("Object_2B_2::Object_2B_2() this:[%p] data_2b_2_ [%d].\n",this,data_2b_2_);
    66     Singleton_2B<Object_2B_1>::instance()->do_something();
    67 };
    68 
    69 void Object_2B_2::do_something()
    70 {
    71     data_2b_2_+= 10000;
    72     printf("Object_2B_2::do_something() this:[%p] data_2b_2_ [%d].\n",this,data_2b_2_);
    73 }
    如代码:Singleton_2B是一个singleton的模板封装,根据这个模版,我们实现了2个单子类,Object_2B_1和Object_2B_2,为了模仿问题,我们在其各自的构造函数里面又都调用了其他一个对象的instance函数。因为我们知道,全局和类static 变量的初始化是编译器自己控制的,我们无法干涉,所以如果假设Object_2B_1::instance_静态成员变量被先构造,他的构造函数调用里面调用Object_2B_2::instance().do_something()函数时,Object_2B_2::instance_可能还没有构造出来。从而导致问题。

    但会导致什么问题呢?崩溃?不一定是,(因为静态数据区的空间应该是先分配的),而且结果这个和编译器的实现有关系,

    GCC的输出结果如下:

    1 //GCC编译器的输出如下:
    2 Object_2B_2::Object_2B_2() this:[0046E1F4] data_2b_2_ [2].
    3 //注意下面,do_something函数被调用了,但是没有崩溃,data_2b_1_默认被初始化为了0
    4 Object_2B_1::do_something() this:[0046E1F0] data_2b_1_ [10000].  
    5 //注意下面,do_something函数被调用了后,构造函数才起作用,data_2b_1_又被初始化为了1,
    6 Object_2B_1::Object_2B_1() this:[0046E1F0] data_2b_1_ [1]. Object_2B_2::do_something() this:[0046E1F4] data_2b_2_ [10002].

    VS2010的DEBUG版本的输出和GCC一致,但有意思的是Realse版本输出结果如下:

    1 //VC++2010的release版本输出 
    2 Object_2B_2::Object_2B_2() this:[0046E1F4] data_2b_2_ [2].
    3 //注意下面的10001,感觉就像构造函数被偷偷调用过一样。有意思。
    4 Object_2B_1::do_something() this:[0046E1F0] data_2b_1_ [10001].  
    5 Object_2B_1::Object_2B_1() this:[0046E1F0] data_2b_1_ [1]. Object_2B_2::do_something() this:[0046E1F4] data_2b_2_ [10002].


     

    2          BOOST的实现如何规避问题

    接着我们就来看看BOOST的模版是使用什么技巧,即保证多线程下不重复初始化,又让相互之间的调用更加安全。

     1 template <typename T>
     2 class Singleton_WY
     3 {
     4 private:
     5     struct object_creator
     6     {
     7         object_creator() 
     8         {
     9             Singleton_WY<T>::instance(); 
    10         }
    11         inline void do_nothing() const {}
    12     };
    13     //利用类的静态对象object_creator的构造初始化,在进入main之前已经调用了instance
    14     //从而避免了多次初始化的问题
    15     static object_creator create_object_;
    16 public:
    17     static T *instance()
    18     {
    19         static T obj;
    20         //do_nothing 是必要的,do_nothing的作用有点意思,
    21         //如果不加create_object_.do_nothing();这句话,在main函数前面
    22         //create_object_的构造函数都不会被调用,instance当然也不会被调用,
    23         //我的估计是模版的延迟实现的特效导致,如果没有这句话,编译器也不会实现
    24     // Singleton_WY<T>::object_creator,所以就会导致这个问题
    25         create_object_.do_nothing();
    26         return &obj;
    27     }
    28 };
    29 //因为create_object_是类的静态变量,必须有一个通用的声明
    30 template <typename T>  typename Singleton_WY<T>::object_creator Singleton_WY<T>::create_object_;
    31 
    32 //测试的例子
    33 class Object_WY_1
    34 {
    35     //其实使用友元帮助我们可以让Object_2B的构造函数是protected的,从而真正实现单子的意图
    36     friend class Singleton_WY<Object_WY_1>;
    37     //注意下面用protected,大家无法构造实例
    38 protected:
    39     Object_WY_1();
    40     ~Object_WY_1(){};
    41 public:
    42     void do_something();
    43 protected:
    44     int data_wy_1_;
    45 };
    46 
    47 class Object_WY_2
    48 {
    49     friend class Singleton_WY<Object_WY_2>;
    50 protected:
    51     Object_WY_2();
    52     ~Object_WY_2(){};
    53 public:
    54     void do_something();
    55 protected:
    56     int data_wy_2_;
    57 };
    58 
    59 //CPP代码
    60 Object_WY_1::Object_WY_1():
    61     data_wy_1_(1)
    62 {
    63     printf("Object_WY_1::Object_WY_1() this:[%p] data_2b_1_ [%d].\n",this,data_wy_1_);
    64     Singleton_WY<Object_WY_2>::instance()->do_something();    
    65 };
    66 
    67 void Object_WY_1::do_something()
    68 {
    69     data_wy_1_+= 10000;
    70     printf("Object_2B_1::do_something() this:[%p] data_2b_1_ [%d].\n",this,data_wy_1_);
    71 
    72 }
    73 
    74 
    75 Object_WY_2::Object_WY_2():
    76     data_wy_2_(2)
    77 {
    78     printf("Object_WY_2::Object_WY_2() this:[%p] data_2b_2_ [%d].\n",this,data_wy_2_);
    79     Singleton_WY<Object_WY_1>::instance()->do_something();
    80 };
    81 
    82 void Object_WY_2::do_something()
    83 {
    84     data_wy_2_+= 10000;
    85     printf("Object_WY_2::do_something() this:[%p] data_2b_2_ [%d].\n",this,data_wy_2_);
    86 }
    调用输出的结果如下,大家可以发现调用顺序正确了Object_WY_2的构造函数内部调用:Singleton_WY<Object_WY_1>::instance()函数的时候,Object_WY_1的单子实例就被创建出来了。
    1 Object_WY_2::Object_WY_2() this:[00ECA138] data_2b_2_ [2].
    2 Object_WY_1::Object_WY_1() this:[00ECA140] data_2b_1_ [1].
    3 Object_WY_2::do_something() this:[00ECA138] data_2b_2_ [10002].
    4 Object_2B_1::do_something() this:[00ECA140] data_2b_1_ [10001].

    首先BOOST的这个实现的Singleton的数据分成两个部分,一个是内部类的object_creator的静态成员creator_object_,一个是instance函数内部的静态变量static T obj;如果外部的有人调用了instance()函数,静态变量obj就会被构造出来,而静态成员creator_object_会在main函数前面构造,他的构造函数内部也调用instance(),这样就会保证静态变量一定会在main函数前面初始化出来。

    到此为止,这部分还都能正常理解,但instance()函数中的这句就是有点诡异技巧的了。

    create_object_.do_nothing();

    其实这句话如果单独分析,并没有明确的作用,因为如果类的静态成员creator_object_的构造就应该让单子对象被初始化。但一旦你注释掉这句话,你会发现create_object_的构造函数都不会被调用。在main函数之前,什么事情都没有发生(VC++2010和GCC都一样),BOOST的代码注释只说是确保create_object_的构造被调用,但也没有明确原因。

    我估计这还是和模版的编译有潜在的关系,模版都是Lazy Evaluation。所以如果编译器没有编译过create_object_.do_nothing();编译器就会漏掉create_object_的对象一切实现,也就完全不会编译Singleton_WY<T>::object_creator和Singleton_WY<T>:: create_object_代码,所以就会导致这个问题。使用dumpbin 分析去掉前后的obj文件,大约可以证明这点。所以create_object_.do_nothing();这行代码必须要有。

    3          个人感觉

    也许是因为我本身对Singleton的模版就不感冒,我对文艺青年的这个Singleton也没有太大胃口,

    一方面是技巧性过强,我不才,do_nothing()那句话的问题我研究了半天。

    二是由于他将所有的instance初始化放在了main函数前面,好处是避免了多线程多次初始化的麻烦,但也限制了初始化的多样性。一些太强的逻辑关系的情况下这招并不好。

    三是这种依靠类static变量的方式,无法按需启动,回收。

    四是性能,每次do_nothine也是无谓的消耗呀。

    为了一个很简单的风险(多次初始化),引入一个技巧性很强的又有各种限制的东东。是否有点画蛇添足。YY。

    告别2012,迎接2013,

    【本文作者是雁渡寒潭,本着自由的精神,你可以在无盈利的情况完整转载此文档,转载时请附上BLOG链接:http://www.cnblogs.com/fullsail/ 或者http://blog.csdn.net/fullsail,否则每字一元,每图一百不讲价。对Baidu文库,360doc加价一倍】

  • 相关阅读:
    iOS开发UI篇—使用storyboard创建导航控制器以及控制器的生命周期
    IOS开发UI篇—导航控制器属性和基本使用
    基于XMPP协议的aSmack源码分析
    XMPP-for-Android
    wecontact
    MyPhone
    Video conference server OpenMCU-ru
    WebRTC_Wrapper
    Sip-MCU
    WebRTC学习笔记
  • 原文地址:https://www.cnblogs.com/fullsail/p/2842618.html
Copyright © 2011-2022 走看看