4

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 [with T = S]: アサーション `px != 0' が失敗しました

コードをトレースすると、コードが静的初期化セグメントを離れると静的 s_instance.t がクリアされ、その後、S::instance を参照するすべてのコードが NULL scoped_ptr を取得することがわかりました。誰も理由を知っていますか?

[更新] すべての静的ファイルを 1 つの 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

4 に答える 4

3

考えられる理由は、 のtemplate <class T> boost::scoped_ptr<T> Singleton<T>::t(0);に staticが初期化されたため、以前に に格納されていた値が 0 に置き換えられたことです。静的変数が構築される順序は、単一のコンパイル単位内でのみ定義されます。あなたの場合、内部でインスタンス化できると思います(または、コンパイル時に両方のファイルで、リンカーは1つだけを選択する必要があります)。で。推測ですが。static volatile S &S_instance = S::instance();ttmain.cppSS.cpp

于 2012-01-13T16:52:36.180 に答える
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++hellocl

于 2012-01-13T16:59:39.310 に答える
0

これは間違っていると思います:

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

インスタンスを作成してから削除するためです。参照ではなくポインタが必要です。

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

私の知る限り、参照は .cpp ファイルの最後で範囲外になります。とにかく、インスタンスが削除されることは想定されていないため、T はそのままのポインターである可能性があります。

于 2012-01-13T17:26:02.157 に答える
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();
}
于 2012-01-13T16:54:09.640 に答える