为什么在单例实现中清除boost :: scoped_ptr

时间:2012-01-13 16:30:25

标签: c++ boost singleton scoped-ptr

我使用boost :: scoped_ptr实现了一个简单的单例:

template <class T> class Singleton : public boost::noncopyable {
   public:
       static T& instance() {
           boost::call_once(init, flag);
           return *t;
       }
       static void init() {
           t.reset(new T());
       }
    private:
       static boost::scoped_ptr <T> t;
      static boost::once_flag flag;
 };
 template <class T> boost::scoped_ptr<T> Singleton<T>::t(0);
 template <class T> boost::once_flag Singleton<T>::flag = BOOST_ONCE_INIT;

定义一个真正的单例类:

 class S : public Singleton<S> {
     public:
        void p() { printf("hello");}
 };

然后我在文件S.cpp中定义一个静态变量:

static volatile S &S_instance = S::instance();

在main.cpp中:

 int main()
 {
      S &ss = S::instance();
      ss.p();
 }

运行此程序,发生异常:

  

/usr/include/boost/smart_ptr/scoped_ptr.hpp:91:T&amp; boost :: scoped_ptr :: operator *()const [with T = S]:断言`px!= 0'失败

跟踪代码,我发现静态s_instance.t在代码离开静态初始化段后被清除,之后所有引用S :: instance的代码都将获得NULL scoped_ptr。有人知道原因吗?

[UPDATE] 我试图将所有静态放入一个cpp文件(S1.cpp):

template <class T> boost::scoped_ptr<T> Singleton<T>::t(0);
template <class T> boost::once_flag Singleton<T>::flag = BOOST_ONCE_INIT;

static S& s_ins = S::instance();

并使用GDB调试它,它看起来遵循我写的顺序。任何想法?

4 个答案:

答案 0 :(得分:3)

可能的原因是静态template <class T> boost::scoped_ptr<T> Singleton<T>::t(0);在<{strong> static volatile S &S_instance = S::instance();后初始化为,因此它将先前存储在t中的值替换为0。构造静态变量的顺序仅在单个编译单元中定义,我猜你的情况t可以在main.cpp内实例化(或者在编译时在两个文件中实例化,并且链接器必须只选择一个)而S驻留在S.cpp中。只是一个猜测。

答案 1 :(得分:0)

如果您删除此行,您的程序可以正常工作(当编译为单个文件时):

static volatile S &S_instance = S::instance();

行。在没有S_instance声明的情况下在我的机器上构建时:

0000000000400d86 <__static_initialization_and_destruction_0(int, int)>:
  400d86:       55                      push   %rbp
  400d87:       48 89 e5                mov    %rsp,%rbp
  400d8a:       48 83 ec 10             sub    $0x10,%rsp
  400d8e:       89 7d fc                mov    %edi,-0x4(%rbp)
  400d91:       89 75 f8                mov    %esi,-0x8(%rbp)
  400d94:       83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
  400d98:       75 43                   jne    400ddd <__static_initialization_and_destruction_0(int, int)+0x57>
  400d9a:       81 7d f8 ff ff 00 00    cmpl   $0xffff,-0x8(%rbp)
  400da1:       75 3a                   jne    400ddd <__static_initialization_and_destruction_0(int, int)+0x57>
  400da3:       b8 b8 40 40 00          mov    $0x4040b8,%eax
  400da8:       0f b6 00                movzbl (%rax),%eax
  400dab:       84 c0                   test   %al,%al
  400dad:       75 2e                   jne    400ddd <__static_initialization_and_destruction_0(int, int)+0x57>
  400daf:       b8 b8 40 40 00          mov    $0x4040b8,%eax
  400db4:       c6 00 01                movb   $0x1,(%rax)
  400db7:       be 00 00 00 00          mov    $0x0,%esi
  400dbc:       bf b0 40 40 00          mov    $0x4040b0,%edi
  400dc1:       e8 3c 05 00 00          callq  401302 <boost::scoped_ptr<S>::scoped_ptr(S*)>
  400dc6:       b8 da 13 40 00          mov    $0x4013da,%eax
  400dcb:       ba 90 40 40 00          mov    $0x404090,%edx
  400dd0:       be b0 40 40 00          mov    $0x4040b0,%esi
  400dd5:       48 89 c7                mov    %rax,%rdi
  400dd8:       e8 8b fd ff ff          callq  400b68 <__cxa_atexit@plt>
  400ddd:       c9                      leaveq 
  400dde:       c3                      retq  

使用您的S_instance声明进行编译时:

0000000000400d86 <__static_initialization_and_destruction_0(int, int)>:
  400d86:       55                      push   %rbp
  400d87:       48 89 e5                mov    %rsp,%rbp
  400d8a:       48 83 ec 10             sub    $0x10,%rsp
  400d8e:       89 7d fc                mov    %edi,-0x4(%rbp)
  400d91:       89 75 f8                mov    %esi,-0x8(%rbp)
  400d94:       83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
  400d98:       75 4f                   jne    400de9 <__static_initialization_and_destruction_0(int, int)+0x63>
  400d9a:       81 7d f8 ff ff 00 00    cmpl   $0xffff,-0x8(%rbp)
  400da1:       75 46                   jne    400de9 <__static_initialization_and_destruction_0(int, int)+0x63>
  400da3:       e8 c2 04 00 00          callq  40126a <Singleton<S>::instance()>
  400da8:       48 89 05 01 33 00 00    mov    %rax,0x3301(%rip)        # 4040b0 <S_instance>
  400daf:       b8 c0 40 40 00          mov    $0x4040c0,%eax
  400db4:       0f b6 00                movzbl (%rax),%eax
  400db7:       84 c0                   test   %al,%al
  400db9:       75 2e                   jne    400de9 <__static_initialization_and_destruction_0(int, int)+0x63>
  400dbb:       b8 c0 40 40 00          mov    $0x4040c0,%eax
  400dc0:       c6 00 01                movb   $0x1,(%rax)
  400dc3:       be 00 00 00 00          mov    $0x0,%esi
  400dc8:       bf b8 40 40 00          mov    $0x4040b8,%edi
  400dcd:       e8 3c 05 00 00          callq  40130e <boost::scoped_ptr<S>::scoped_ptr(S*)>
  400dd2:       b8 e6 13 40 00          mov    $0x4013e6,%eax
  400dd7:       ba 90 40 40 00          mov    $0x404090,%edx
  400ddc:       be b8 40 40 00          mov    $0x4040b8,%esi
  400de1:       48 89 c7                mov    %rax,%rdi
  400de4:       e8 7f fd ff ff          callq  400b68 <__cxa_atexit@plt>
  400de9:       c9                      leaveq 
  400dea:       c3                      retq   

在后面的代码中,您可以清楚地看到静态scoped_ptr的构造函数发生在S_instance之后。

上述内容摘自:

#include <cstdio>

#include <boost/scoped_ptr.hpp>
#include <boost/thread/once.hpp>
#include <boost/noncopyable.hpp>

template <class T> class Singleton : public boost::noncopyable {
public:
    static T& instance() {
        boost::call_once(init, flag);
        return *t;
    }
    static void init() {
        t.reset(new T());
    }
private:
    static boost::scoped_ptr <T> t;
    static boost::once_flag flag;
};

template <class T> boost::scoped_ptr<T> Singleton<T>::t(0);
template <class T> boost::once_flag Singleton<T>::flag = BOOST_ONCE_INIT;

class S : public Singleton<S> {
public:
    void p() { printf("hello");}
};

// static volatile S &S_instance = S::instance();

int main()
{
    S &ss = S::instance();
    ss.p();
}

答案 2 :(得分:0)

由于全局变量的未定义创建顺序,我很确定它是未定义的行为。因此,您的S_instance首先进行初始化,然后template <class T> boost::scoped_ptr<T> Singleton<T>::t(0)

这样一个简单的程序可以说明,当订单逆转时会发生什么:

#include <iostream>

std::string &getS();
std::string& t = getS();
std::string s("hello");
std::string &getS() {s = "world"; return s;}

int main()
{
    std::cout << t;
}

好吧,g++让我崩溃,并hello打印cl

答案 3 :(得分:0)

我认为这是错误的:

static volatile S &S_instance = S::instance();

因为它创建然后删除实例。你想要一个指针,而不是一个参考。

static S *S_instance = &S::instance();

据我所知,引用超出了.cpp文件末尾的范围。无论如何都不会删除该实例,因此T只能是一个裸指针。