2013-04-23 22 views
5

ファクトリクラスを実装しているうちに、私が理解できないような動作のあるstd::auto_ptrが発生しました。問題を次の小さなプログラムに還元したので、始めましょう。シングルトンパターン:auto_ptrとunique_ptrの動作が異なります

次シングルトンクラスを考えてみましょう。ここでは、リソースを管理するスマートポインタの使用は、主によって決定される

#ifndef SINGLETON_H_ 
#define SINGLETON_H_ 

#include<iostream> 
#include<memory> 

class singleton { 
public: 
    static singleton* get() { 
    std::cout << "singleton::get()" << std::endl; 
    if (!ptr_.get()) { 
     std::cout << &ptr_ << std::endl; 
     ptr_.reset(new singleton ); 
     std::cout << "CREATED" << std::endl; 
    } 
    return ptr_.get(); 
    } 

    ~singleton(){ 
    std::cout << "DELETED" << std::endl; 
    } 
private: 
    singleton() {} 
    singleton(const singleton&){} 

    static std::auto_ptr<singleton> ptr_; 
    //static std::unique_ptr<singleton> ptr_; 
}; 

#endif 

singleton.h singleton.cpp

#include<singleton.h>o 
std::auto_ptr<singleton> singleton::ptr_(0); 
//std::unique_ptr<singleton> singleton::ptr_; 

プログラムの終了時に漏れを避ける必要があります。

a.h

#ifndef A_H_ 
#define A_H_ 

int foo(); 

#endif 

a.cpp

#include<singleton.h> 

namespace { 
    singleton * dummy(singleton::get()); 
} 

int foo() { 
    singleton * pt = singleton::get(); 
    return 0; 
} 

main.cppに今

#include<a.h> 

int main() { 

    int a = foo(); 

    return 0; 
} 

面白い一部:私は、以下のプログラムでは、このコードを使用します。私は別に3つのソースをコンパイルします。

$ g++ -I./ singleton.cpp -c 
$ g++ -I./ a.cpp -c 
$ g++ -I./ main.cpp -c 

私はこの順序で明示的にリンクする場合:私は期待どおり

$ g++ main.o singleton.o a.o 

すべての仕事、と私はstdoutに以下の取得:

singleton::get() 
0x804a0d4 
CREATED 
singleton::get() 
DELETED 

代わりに、この注文を使用してソースをリンクする場合:

$ g++ a.o main.o singleton.o 

私はこの出力を得る:

singleton::get() 
0x804a0dc 
CREATED 
singleton::get() 
0x804a0dc 
CREATED 
DELETED 

私は別のコンパイラのブランド(IntelおよびGNU)とバージョンを試してみましたが、この動作は、それらの中で一貫しています。とにかく、私はリンクの順序に依存するコードを見ることができません。

さらに、auto_ptrunique_ptrに置き換えられた場合、その動作は正しいものと常に一致します。

それは私に質問になります:ここで何が起こっているのか誰かが手がかりを持っていますか?

+0

あなたのg ++​​バージョンは何ですか? –

+2

あなたはおそらく[この質問](http://stackoverflow.com/questions/86582/)を読んでみたいです。 – fredoverflow

答えて

4

dummyおよびstd::auto_ptr<singleton> singleton::ptr_(0)が構成される順序は不明である。 auto_ptrケースでは

あなたがdummysingleton::ptr_(0)を構築する場合、dummy呼び出しで作成された値は、ptr_(0)のコンストラクタによって消去されます。

ptr_(([](){ std::cout << "made ptr_\n"; }(),0));などでptr_の構成にトラッキングを追加します。

それはunique_ptrで動作するという事実は偶然です、そしてそのようなものが何もしないと、おそらく最適化のためにunique_ptr(0)は(staticデータは建設が開始される前にゼロにされ、それがゼロに把握できるので、コンパイラはunique_ptr(0)ことを把握することができればメモリをゼロにするだけで、合法的にコンストラクタをスキップすることができます。つまり、メモリがゼロにならなくなります。

static std::auto_ptr<singleton>& get_ptr() { 
    static std::auto_ptr<singleton> ptr_(0); 
    return ptr_; 
    } 

get_ptr()ptr_への参照を置き換えます。これを修正するために

一つの方法は、次のような、使用前に建設を保証する方法を使用することです。

3

異なる翻訳単位で定義されたファイルスコープオブジェクトの構築の順番は不明です。しかしながら、典型的には、別の翻訳単位の前にリンクされている翻訳単位で定義されたオブジェクトは、第2翻訳単位で定義されたオブジェクトの前に構築される。ここでの違いは、a.osingleton.oがリンクされている順序です。 singleton.oa.oの前にリンクされている場合、dummyより前にsingleton::ptr_が初期化され、すべて正常です。最初にa.oがリンクされると、dummyが最初に初期化され、シングルトンが構築されます。 singleton::ptr_は0に初期化され、singletonの元のコピーへのポインタが破棄されます。その後、fooの呼び出しで、singleton::get()を呼び出すと、シングルトンが再度構築されます。

+0

説明に感謝します。私は2つの答えを受け入れることができないことを後悔します... – Massimiliano

関連する問題