почему boost :: scoped_ptr очищается в одноэлементной реализации - PullRequest
4 голосов
/ 13 января 2012

Я реализовал простой синглтон, используя 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 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 ]

3 голосов
/ 13 января 2012

Возможная причина в том, что статический 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.Просто предположение, хотя.

0 голосов
/ 13 января 2012

Я думаю, что это неправильно:

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

, потому что он создает, а затем удаляет экземпляр.Вам нужен указатель, а не ссылка.

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

Насколько я знаю, ссылка выходит из области видимости в конце файла .cpp.Ожидается, что экземпляр в любом случае не будет удален, поэтому T может быть просто указателем.

0 голосов
/ 13 января 2012

Я почти уверен, что это неопределенное поведение из-за неопределенного порядка создания глобальных переменных. Итак, сначала 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

0 голосов
/ 13 января 2012

Ваша программа работает правильно (при компиляции в один файл), если вы удалите эту строку:

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

OK. При сборке на моей машине без вашего объявления 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();
}
...