2017-07-27 6 views
1

これは別のプロジェクトで発生した問題を単純化したものです。要素をセットから削除するときに下付き文字

は、私は以下のクラスがあるとしましょう:

class MyClass { 

public: 
    MyClass() { 
     std::cout << "MyClass constructed\n"; 
     Instances().insert(this); 
    } 
    ~MyClass() { 
     std::cout << "MyClass destructed\n"; 
     Instances().erase(this); 
    } 

    static std::unordered_set<MyClass*>& Instances() { 
     static std::unordered_set<MyClass*> _instances; 
     return _instances; 
    } 

}; 

それはそれは、クラスの既存のインスタンスを追跡するために使用する静的unordered_setを持っています。インスタンスが構築されると、そのアドレスがセットに追加されます。インスタンスが破棄されると、そのアドレスがセットから削除されます。

は今、私はMyClassのインスタンスを含むshared_ptr秒のvectorを持つ別のクラスを持っている:

struct InstanceContainer { 
    std::vector<std::shared_ptr<MyClass>> instances; 
}; 

ここでのキーポイントはmain上記のこのクラスのグローバルインスタンスが存在することです。 mainのクラスを宣言しても問題は発生しないため、これは問題の一部と考えられます。

mainの内部では、私は(と言うInstanceContainerのグローバルインスタンスがcontainerと呼ばれている)次のようにします。プログラムが終了するまで、私は読み取りアクセス違反(「ベクトル添字を得るとき

container.instances.emplace_back(std::shared_ptr<MyClass>(new MyClass)); 

すべては、罰金です範囲外です。)Instances().erase(this)MyClassのデストラクタで実行されます。

_instancesからインスタンスを複数回消去しようとしていた可能性があります(したがって、cout) - しかし、コンストラクタは一度だけ呼び出され、デストラクタは一度だけ呼び出されます。私はこれが起こるとき、_instances.size()0に等しいことがわかった。奇妙なことは、eraseを呼び出す前は0と等しいことです。何かがセットから消去される前に、それは空ですか?

私の理論は、これは、プログラムが終了するとオブジェクトが破壊される順序と関係しているということです。おそらく、静的な_instancesは、MyClassのデストラクタが呼び出される前に解放されています。

私は、誰かがこれについていくつかの光を当てて、それが起こっているかどうかを確かめることができると考えていました。

私の回避策は、消去しようとする前に_instances.size()0であるかどうかを確認することです。これは安全ですか?そうでない場合は、他に何ができますか?

重要な場合は、MSVCを使用しています。 executable exampleです

答えて

1

ここで何が起こりますか。 mainが入力される前に、そのグローバル変数InstanceContainerが最初に構築されます。ファンクション静的変数_instancesは、Instances()が初めて呼び出されたときに後で作成されます。

プログラムのシャットダウン時に、これらのオブジェクトのデストラクタが逆の順序で呼び出されます。したがって、_instancesが最初に破棄され、次にInstanceContainerが破棄され、共有ポインタのベクトルが破棄され、ベクトル内にまだ残っているすべてのオブジェクトに対して~MyClassが実行され、既に破棄された_instances_instances.erase()が呼び出されます。あなたのプログラムは、寿命が終了したオブジェクトにアクセスすることによって未定義の動作を示します。

これを回避するにはいくつかの方法があります。 1つは、mainが返される前にInstanceContainer::instancesが空であることを確認できます。 InstanceContainerがあなたのデザインでどのような役割を果たしているかを決して説明していないので、これがどれだけ実現可能かは考えられません。

つ、あなたはヒープ上_instancesを割り当て、それをリークすることができます:

static std::unordered_set<MyClass*>& Instances() { 
    static auto* _instances = new std::unordered_set<MyClass*>; 
    return *_instances; 
} 

これはグローバルオブジェクトの破壊によって生き、それを維持します。

三、あなたはInstanceContainerグローバル変数の定義の前にこのようなものを入れることができます:

static int dummy = (MyClass::Instances(), 0); 

これは_instancesは、以前に作成したので、後に破棄されることを保証します。

関連する問題