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

why boost::scoped_ptr is cleared in a singleton implementation

本文关键字:boost scopedptr 清除 单例 实现 为什么      更新时间:2023-10-16

我使用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&boost::scoped_ptr::operator*()const[带T=S]:断言`px!=0'失败

跟踪代码,我发现一旦代码离开静态初始化段,静态s_instance.t就会被清除,之后所有引用s::实例的代码都会得到NULL scope_ptr。有人知道原因吗?

[更新]我试图将所有静态文件放入一个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调试它,它看起来遵循我写的顺序。知道吗?

一个可能的原因是静态template <class T> boost::scoped_ptr<T> Singleton<T>::t(0);static volatile S &S_instance = S::instance();之后被初始化,因此它用0替换了先前存储在t中的值。静态变量的构造顺序只在一个编译单元中定义,我猜在您的情况下,t可以在main.cpp中实例化(或者更确切地说,在编译时在两个文件中实例化,链接器只需要选择一个),而S位于S.cpp中。不过只是猜测。

我很确定这是未定义的行为,因为全局变量的创建顺序未定义。因此,首先初始化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++崩溃了,用cl 打印hello

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

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   

在后一段代码中,您可以清楚地看到静态scope_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();
}

我认为这是错误的:

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

因为它创建然后删除实例。您需要的是指针,而不是引用。

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

据我所知,该引用在.cpp文件末尾超出了作用域。无论如何都不希望删除该实例,因此T可以只是一个空指针。