2

(私にとっては) 奇妙なアクセス違反の例外が表示されます。できるだけ問題を減らすようにします。クラス A とシングルトン オブジェクト sing_ があります。コードは次のようになります。

class A {
    A();
    Sing& sing_;
}

A::A() : sing_(Sing::instance()){
    call a method that creates a local copy of Singleton Sing.
    ....
}

Sing クラスは Singleton から継承します。

class Sing : public Singleton<Sing>{
    friend class Singleton<Sing>;
    ....
}

シングルトン自体はそのように見えます(これはQuantLibライブラリでの実装です)

template <class T>
class Singleton : private boost::noncopyable {
  public:
    static T& instance();
  protected:
    Singleton() {}
};


template <class T>
T& Singleton<T>::instance() {
    static std::map<Integer, boost::shared_ptr<T> > instances_;
    #if defined(QL_ENABLE_SESSIONS)
    Integer id = sessionId();
    #else
    Integer id = 0;
    #endif
    boost::shared_ptr<T>& instance = instances_[id];
    if (!instance)
        instance = boost::shared_ptr<T>(new T);
    return *instance;
}

私のプロジェクト コードは Qt GUI 環境に埋め込まれています。デバッグモードで起動しても問題ありません。リリースモードで起動しようとすると、状況がひどく変わります。これが主な方法です。

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    GUI w;
    w.show();
    w.setup(argc, argv);

    return a.exec();
}

最後に、クラス GUI は次のように省略されます。

class GUI : public QMainWindow
{
    Q_OBJECT

public:
    GUI(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~GUI();
private:
    boost::shared_ptr<A> a_; 
};

このコードをリリース モードで開始すると、次のことが起こります。

  1. ___tmainCRTstartup() というメソッドが呼び出されます。そこで、メソッド _WinMain が呼び出されます。

  2. このメソッドWinMain 内で (main メソッドが呼び出されて GUI オブジェクトが作成される前であっても)、A のコンストラクターが呼び出されます。これは、sing メンバーが初期化されることを意味します。

  3. sing_ は Sing::instance() の呼び出し中に初期化されます。これまでのところ、すべて問題ないようです。

  4. A のコンストラクターが実行されます。その中で、シングルトン オブジェクト Sing へのローカル参照が作成されます。Sing::instance() を呼び出すと、その行でアクセス違反が発生します

        boost::shared_ptr<T>& instance = instances_[id];
    

その場所 (リリース モードでデバッグ) の instances_[id] を見ると、マップはかなり破壊されているように見えます。これは、マップに 1 つの要素があることを意味します。しかし、キーは 0 ではなく、非常に負の整数であり、値も奇妙に見えます。

ここで何がうまくいかないのか、私にはまったくわかりません。

A のメンバー sing_ を静的メンバーに変更すると、問題が修正されます。

class A {
    A();
    static Sing& sing_;
}

Sing& sing_ = Sing::instance();

A::A() {
    call a method that creates a local copy of Singleton Sing.
    ....
}

それはもちろん素晴らしいことですが、これら 2 つの実装の「大きな」違いは何なのか、本当に疑問に思っています。最初の方法がアクセス違反で終わるのはなぜですか? どんなヒントでも大歓迎です。

4

1 に答える 1

0

問題のルートについてはわかりませんが、そこにたどり着くための助けを提供できます。

まず、デバッグで実行されるプログラムがリリースで失敗する (およびその逆) 最も一般的な理由は次のとおりです。

  1. デバッグでは、通常、すべてのメモリ (私が知っているほとんどのデバッガー) は 0 として初期化されます。したがって、安全な nullptr チェックはエラーを回避します。リリースではメモリは初期化されないため、ガベージ/ランダム値が取得されます。

  2. タイミング。コードの最適化、およびデバッグ チェックと設定の欠如 (すべてを 0 に設定するなど) により、コードが高速化されるため、スレッド間で異なるタイミングが生じます。

あなたのケースに戻りましょう。変数が「破壊された」と表示される理由は、おそらくコードの最適化のためです。
通常、コードの最適化を有効にしている場合、コンパイラは、意図したとおりにスタックではなく、ハードウェア レジスタにいくつかの変数を配置することを決定する場合があります。
これにより、デバッガーが一部のローカル変数を誤って解釈する可能性があります。(最も一般的には、*this ポインターはレジスターに格納されます)。

現在、マップ内の operator[] は例外を発生させません。マップにそのキーの値がない場合は、マップが作成されます。

したがって、私が考えることができる唯一の説明は、マップ自体が破損しているため、マップのノードを反復しようとしているときにクラッシュするということです。

このタイプの破損は通常、2 つのスレッドが同時にマップを変更しようとすることによって発生します。
その単純なシングルトン実装にはロック保護がないため、これは可能です。
これが当てはまる可能性がある別の兆候は、静的にすると問題が解決されることです。変数を静的にすると、オブジェクトの開始がはるかに早く発生します。これは、スレッドの競合を解決するために必要なタイミングにすぎない可能性があります。

于 2013-03-08T17:57:49.527 に答える