我使用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调试它,它看起来遵循我写的顺序。任何想法?
答案 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只能是一个裸指针。