6

私は、定期的に(newを使用して)作成および破棄されるコントローラーオブジェクトを内部的に持つC++アプリに取り組んでいます。これらのコントローラーは、自分自身を別のオブジェクト (controllerSupervisor と呼びましょう) に登録し、破棄されたときに自分自身を登録解除する必要があります。

私が現在直面している問題は、アプリケーションを終了したときに発生しています。破壊の順序は決定論的ではないため、コントローラー自体の (一部の) 前に単一の controllerSupervisor インスタンスが破壊され、それらが unregister メソッドを呼び出したときに発生します。それらのデストラクタは、すでに破壊されたオブジェクトに対して行います。

これまでに私が思いついた唯一のアイデア (大きな風邪をひいているので、これはあまり意味がないかもしれません) は、controllerSupervisor をスタック上のグローバル変数としてではなく、ヒープ上に (つまり new を使用して) 持つことです。ただし、その場合、削除する場所がありません(これはすべてサードパーティの種類のライブラリにあります)。

可能なオプションについてのヒント/提案をいただければ幸いです。

4

15 に答える 15

5

自動変数 (関数で使用する「通常の」ローカル変数を含む) の破棄の順序は、それらの作成の逆順です。そのため、controllerSupervisor を一番上に配置します。

グローバルの破棄の順序も作成の逆であり、これはグローバルが定義された順序に依存します。後で定義されたオブジェクトは後で作成されます。ただし、注意してください: 異なる .cpp ファイル (翻訳単位) で定義されたオブジェクトは、定義された順序で作成されるとは限りません。

マイクが推奨する方法で使用することを検討する必要があると思います。

  1. 作成は、関数静的スーパーバイザ オブジェクトへのポインタを返すことにより、最初の使用時にシングルトン パターンを使用して行われます (異なる翻訳単位のオブジェクトの初期化順序が定義されていないため)。
  2. スーパーバイザーは通常、破棄されます (関数内の static の破棄に関する規則を使用して)。コントローラは、スーパーバイザの静的関数を使用して登録解除します。その 1 つは、スーパーバイザーが既に破棄されているかどうかをチェックします ( のポインターをチェックします!= 0)。そうである場合、何も行われません。それ以外の場合は、スーパーバイザーに通知されます。

コントローラーが接続されていないスーパーバイザーが存在する可能性があると考えているため (一時的な場合のみ)、スマート ポインターを使用してスーパーバイザーを自動的に破棄することはできませんでした。

于 2008-11-13T20:47:39.977 に答える
5

Alexandrescu の Modern C++ Design (Chaper 6, Singletons) には、基本的にこのトピックに関する章全体があります。彼は、シングルトン同士の間でも依存関係を管理できるシングルトン クラスを定義しています。

ところで、本全体も強くお勧めします。

于 2008-11-13T21:36:37.117 に答える
2

オブザーバー パターンを使用できます。コントローラーは、それが破壊されていることをスーパーバイザーに伝えます。スーパーバイザーは、破壊時にその子に同じことを伝えます。

http://en.wikipedia.org/wiki/Observer_patternをご覧ください

于 2008-11-13T20:51:16.133 に答える
1

GNU gcc / g ++は、非常に便利な型の移植性のない属性を提供します。これらの属性の1つは、グローバルオブジェクトが構築される順序を定義するinit_priorityであり、その結果、オブジェクトが破棄される順序とは逆になります。男から:

init_priority(PRIORITY)

 In Standard C++, objects defined at namespace scope are guaranteed
 to be initialized in an order in strict accordance with that of
 their definitions _in a given translation unit_.  No guarantee is
 made for initializations across translation units.  However, GNU
 C++ allows users to control the order of initialization of objects
 defined at namespace scope with the init_priority attribute by
 specifying a relative PRIORITY, a constant integral expression
 currently bounded between 101 and 65535 inclusive.  Lower numbers
 indicate a higher priority.

 In the following example, `A' would normally be created before
 `B', but the `init_priority' attribute has reversed that order:

      Some_Class  A  __attribute__ ((init_priority (2000)));
      Some_Class  B  __attribute__ ((init_priority (543)));

 Note that the particular values of PRIORITY do not matter; only
 their relative ordering.
于 2008-11-13T22:26:03.523 に答える
1

いくつかの提案:

  • controllerSupervisor を、ポインターを返す静的メソッドを介してアクセスされるシングルトンにする (または、その目的のために作成するシングルトン オブジェクトにラップする) と、登録されたオブジェクトの dtors が静的アクセサーを呼び出すことができます (アプリケーションの場合shutdown および controllerSupervisor が破棄された場合は NULL が返されます)、これらのオブジェクトはその場合、登録解除メソッドの呼び出しを回避できます。

  • new を使用してヒープ上に controllerSupervisor を作成し、boost::shared_ptr<>その寿命を管理するようなものを使用します。シングルトンshared_ptr<>の static アクセサー メソッドで を渡します。

于 2008-11-13T21:04:45.030 に答える
0

醜いですが、これは最も簡単な方法かもしれません:

登録解除の呼び出しをキャッチしてみてください。多くのコードを変更する必要はありません。アプリはすでにシャットダウンされているため、大したことではありません。(または、シャットダウンの順序について他の批准がありますか?)

他の人はより良いデザインを指摘していますが、これは単純です。(そして醜い)

この場合もオブザーバーパターンが好きです。

于 2008-11-13T22:12:47.637 に答える
0

C ++標準では、問題の変数がすべて1つのファイル(「変換単位」)に収まる場合の初期化/破棄の順序が指定されています。複数のファイルにまたがるすべてのものは、移植性がなくなります。

次に、スーパーバイザーに各コントローラーを破棄させるという提案をします。これは、スーパーバイザーだけが誰かに自分自身を破壊するように指示する(誰も自分自身を破壊しない)という意味でスレッドセーフであるため、競合状態はありません。また、デッドロックの可能性を回避する必要があります(ヒント:スーパーバイザーから何も必要とせずに、コントローラーに指示されたら、コントローラーが自分自身を破壊できることを確認してください)。


プログラムが終了する前にコントローラーを破棄する必要がある場合(つまり、コントローラーが短命である可能性がある場合)でも、このスレッドを安全にすることができます。

まず、私が自分自身を破壊することを決定したかどうかを心配するのは競合状態ではない可能性があり、マイクロ秒後にスーパーバイザーが私を破壊することを決定し、そのように指示します。

次に、この競合状態が心配な場合は、たとえば、すべての破棄要求がスーパーバイザーを経由するように要求することで、競合状態を修正できます。自分自身を破壊したいのですが、スーパーバイザーに指示するか、その意図をスーパーバイザーに登録します。スーパーバイザーを含む他の誰かが私を破壊したい場合、彼らはスーパーバイザーを通してそれを行います。

于 2008-11-13T23:07:33.580 に答える
0

イベントを使用してコントローラの破壊を知らせることができます

すべてのコントローラが破棄されるまで待機する Supervisor のデストラクタに WaitForMultipleObjects を追加します。

コントローラーのデストラクタでは、コントローラーの終了イベントを発生させることができます。

各コントローラーの終了イベント ハンドルのグローバル配列を維持する必要があります。

于 2008-11-13T22:51:25.407 に答える
0

スーパーバイザーにコントローラーの破壊を任せるのはどうですか?

于 2008-11-13T21:52:23.873 に答える
0

この質問の見出しを読んだとき、私はすぐに自問しました。

于 2008-11-19T15:11:22.680 に答える
0

状況に応じて、次のいずれかを行うことができます。

  1. gurin が提案する Observer パターンを使用します。基本的に、スーパーバイザはコントローラにダウンしていることを通知します...
  2. スーパーバイザーにコントローラーを「所有」させ、ダウンしたときにコントローラーを破壊する責任を負わせます。
  3. コントローラーを shared_pointers に保持して、最後にダウンした人が実際の破壊を行うようにします。
  4. (へのスマート ポインター) コントローラーとスタック上のスーパーバイザーの両方を管理して、破棄の順序を決定できるようにします。
  5. その他...
于 2008-11-13T20:58:44.293 に答える
0

登録されたコントローラーの数を、実際の削除のセンチネルとして使用することを検討できます。

削除呼び出しは単なる要求であり、コントローラーが登録解除されるまで待つ必要があります。

前述のように、これはオブザーバー パターンの 1 つの使用法です。

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
        inDeleteMode_ = true;
        if( controllers_.empty()){
            delete this;
        }
    }

    void deregister(Controller* controller) {
        controllers_.erase(
            remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
        if( inDeleteMode_ && controllers_.empty()){
            delete this;
        }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();
于 2008-11-13T20:59:14.027 に答える
0

正確にはエレガントではありませんが、次のようなことができます。

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

新しいグローバル:

ControllerCoordinator control;

コントローラーを作成するすべての場所に、 を追加しcontrol.supervisor.insert(controller)ます。1つを破壊するすべての場所に、 を追加しcontrol.erase(controller)ます。control.control.supervisor へのグローバル参照を追加することで、プレフィックスを回避できる場合があります。

コーディネーターのスーパーバイザー メンバーは、デストラクターが実行されるまで破棄されないため、スーパーバイザーがコントローラーよりも長く存続することが保証されます。

于 2008-11-13T21:02:18.997 に答える
0

制御スーパーバイザーをシングルトンにします。コントロールコンストラクターが構築中にスーパーバイザーを取得することを確認してください (あとがきではありません)。これにより、コントロール スーパーバイザがコントロールの前に完全に構​​築されることが保証されます。したがって、デストラクタは、制御スーパーバイザ デストラクタの前に呼び出されます。

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };
于 2008-11-13T21:20:16.793 に答える
0

OK、他の場所で提案されているように、スーパーバイザーをシングルトン (または同様に制御されたオブジェクト、つまりセッションにスコープされたオブジェクト) にします。

必要に応じて、シングルトンの周りに適切なガード (スレッド化など) を使用します。

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
        controller_(NULL)
        {
            controller_ = Controller::create();
        }

    ~ControllerClient() {
        delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {        
        if (!theSupervisor ) {
            theSupervisor = Supervisor();
        }
        return *theSupervisor;
    } 

    void deregister(Controller& controller) {
        remove( controller );
        if( controllers_.empty() ) {
            theSupervisor = NULL;
            delete this;
        }       
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
        return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
        supervisor_->deregister(*this);
        supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
        supervisor_(&supervisor)
        {}
}
于 2008-11-13T22:06:14.373 に答える