39

次の方法でシングルトン C++ を作成します。

class A {
    private:
        static A* m_pA;
        A();
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL)
        m_pA = new A();
    return m_pA;
}

A::~A() {
    FreeInstance()  // Can I write this? are there any potential error?
}

void A::FreeInstance() {
    delete m_pA;
    m_pA = NULL;
}

ありがとう!Evan Teran と sep61.myopenid.com の答えは正しく、本当に良いです! 私のやり方は間違っています。そのようなコードを書いている人なら誰でも私のばかげた間違いを避けることができれば幸いです。

私のプロジェクトのシングルトン A にはスマート ポインターのベクトルがあり、別のスレッドでもこのベクトルを編集できるため、アプリケーションを閉じると、CMutex をたくさん追加しても常に不安定になります。マルチスレッド エラー + シングルトン エラーで 1 日無駄になりました。

//------------------------------------------------ ----------- 新しいシングルトンです。次のサンプルに問題があると思われる場合は、自由に編集してください。

class A {
    private:
        static A* m_pA;
        explicit A();
        void A(const A& a);
        void A(A &a);
        const A& operator=(const A& a);
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL){
        static A self;
        m_pA = &self;
    }
    return m_pA;
}

A::~A() {
}
4

10 に答える 10

206

誰もがシングルトンをポインタとして返したいのはなぜですか?
参照として返す方がはるかに論理的です!

シングルトンを手動で解放することはできません。誰がシングルトンへの参照を維持しているかをどのように知ることができますか? 誰も参照を持っていない(または保証できない)場合(あなたの場合はポインターを介して)、オブジェクトを解放するビジネスはありません。

関数メソッドで static を使用します。
これにより、一度だけ作成および破棄されることが保証されます。また、遅延初期化を無料で提供します。

class S
{
    public:
        static S& getInstance()
        {
            static S    instance;
            return instance;
        }
    private:
        S() {}
        S(S const&);              // Don't Implement.
        void operator=(S const&); // Don't implement
 };

また、コンストラクターを非公開にする必要があることに注意してください。また、シングルトンのコピーを作成できないように、デフォルトのコピー コンストラクターと代入演算子をオーバーライドしてください (そうしないと、シングルトンにはなりません)。

また読む:

正しい理由でシングルトンを使用していることを確認するため。

一般的なケースでは技術的にスレッドセーフではありませんが、次を参照してください:
C++ 関数の静的変数の有効期間は何ですか?

GCC には、これを補う明示的なパッチがあります:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html

于 2008-11-07T02:51:26.193 に答える
13

次のような静的オブジェクトを使用すると、削除する必要がなくなります。

if(m_pA == 0) {
    static A static_instance;
    m_pA = &static_instance;
}
于 2008-11-07T01:16:56.947 に答える
4

C++ のシングルトンは次のように記述できます。

static A* A::GetInstance() {
    static A sin;
    return &sin;
}
于 2008-11-07T01:22:30.010 に答える
3

コピー コンストラクターと代入演算子をプライベートにすることを忘れないでください。

于 2008-11-07T01:24:40.237 に答える
1

マイヤーズスタイルのシングルトン(以前の回答のいくつかのようにローカル静的オブジェクトを使用)に熱狂した後、私は複雑なアプリのライフタイム管理の問題に完全にうんざりしました。

私は、アプリの初期化の早い段階で意図的に「インスタンス」メソッドを参照して、必要なときに作成されていることを確認し、予測できない(または少なくとも非常に複雑でやや隠されている)物事が破壊される順序。

YMMVはもちろん、シングルトン自体の性質にも少し依存しますが、巧妙なシングルトン(および巧妙さを取り巻くスレッド/ロックの問題)に関する多くのワッフルは、IMOを過大評価しています。

于 2008-11-07T11:41:25.223 に答える
1

「ModernC++Design」を読むと、静的変数を返すよりもシングルトン設計がはるかに複雑になる可能性があることがわかります。

于 2008-11-07T11:56:00.373 に答える
1

その行番号を書く理由はないと思います。デストラクタ メソッドは静的ではなく、シングルトン インスタンスはその方法で破棄されません。オブジェクトをクリーンアップする必要がある場合は、作成済みの静的メソッド FreeInstance() を使用します。

それ以外は、私が作成したのとほぼ同じ方法でシングルトンを作成します。

于 2008-11-07T01:20:24.507 に答える
0

この実装は、次の質問に答えることができれば問題ありません。

  1. オブジェクトがいつ作成されるか知っていますか (new の代わりに static オブジェクトを使用する場合? main() はありますか?)

  2. シングルトンには、作成されるまでに準備ができていない可能性のある依存関係がありますか? new の代わりに static オブジェクトを使用する場合、この時点でどのライブラリが初期化されていますか? それらを必要とする可能性のあるコンストラクターでオブジェクトは何をしますか?

  3. いつ削除されますか?

new() を使用すると、オブジェクトがいつどこで作成および削除されるかを制御できるため、より安全です。しかし、それを明示的に削除する必要があり、おそらくシステム内の誰もいつ削除するかを知りません。意味がある場合は、 atexit() を使用できます。

メソッドで静的オブジェクトを使用するということは、それがいつ作成または削除されるかを実際には知らないことを意味します。名前空間でグローバルな静的オブジェクトを使用して、 getInstance() をまったく回避することもできます-それはあまり追加しません。

スレッドを使用すると、大きな問題が発生します。次の理由により、C++ で使用可能なスレッド セーフなシングルトンを作成することは事実上不可能です。

  1. getInstance の永久ロックは非常に重い - getInstance() ごとに完全なコンテキスト スイッチ
  2. ダブル チェック ロックは、コンパイラの最適化とキャッシュ/弱いメモリ モデルが原因で失敗し、実装が非常に難しく、テストが不可能です。アーキテクチャをよく知っていて、移植できないようにしたくない場合を除き、実際のシステムでそれを実行しようとはしません。

これらは簡単に Google で検索できますが、弱いメモリ モデルに関する適切なリンクは次のとおりです: http://ridiculousfish.com/blog/archives/2007/02/17/barrier

解決策の 1 つは、ロックを使用することですが、ユーザーは getInctance() から取得したポインターをキャッシュし、getInstance() が重くなるのに備える必要があります。

もう 1 つの解決策は、ユーザーが自分でスレッド セーフを処理できるようにすることです。

さらに別の解決策として、単純なロック機能を備えた関数を使用し、 new() が呼び出された後にロックとチェックを行わずに別の関数に置き換えることもできます。これは機能しますが、完全な実装は複雑です。

于 2008-11-07T12:10:25.070 に答える
0

パターンに基づく優れた C++ ライブラリ ACE があります。さまざまな種類のパターンに関するドキュメントがたくさんあるので、彼らの作品を見てください: http://www.cs.wustl.edu/~schmidt/ACE.html

于 2008-11-07T11:10:57.403 に答える