1

必要なものに特別なキーワードがあるかどうかわかりません。私は基本的な観察パターンを書いていますが、いくつかの問題が心配です。私の実装は古典的です。私はオブザーバーの std::set を使用しています。イベントを発生させる必要があるときはいつでも、このセットを繰り返し処理し、各オブザーバーの通知メソッドを呼び出します。私の問題は次のとおりです。次の場合、監視可能なオブジェクトがオブザーバーにイベントを送信するとどうなりますか。

  • あるオブザーバーが、イベント中に設定されたオブザーバーから自分自身 (または他のオブザーバー) を削除したいですか?
  • 1 人のオブザーバーがオブザーバー セットをクリアしたい (すべてのオブザーバーを削除する) ?
  • 1人のオブザーバーがオブザーバブルオブジェクトを破壊しますか?

私は、これらすべてのケースが最終的に起こることを知っています。3 つ目のアイデアはありますが、これはトピック外です。最初と 2 番目のケースの問題は、std::set を削除またはクリアすると、オブザーバブルを列挙するために使用しているイテレータが無効になることです。そうでない場合でも、オブザーバブルは、イベント処理中に削除されたオブザーバーに通知するべきではありません。

アイテムが削除されたときに有効な状態を維持できるイテレータを提供するセットの実装はまだ見つかりませんでした。ただし、必要に応じて更新するために、間接的であり、コンテナに生きているイテレータへの参照を格納するという犠牲を払って、それを実装することは可能です。

別の解決策は、オブザーバーのセットをコピーし、コピーを反復して、現在反復されているオブザーバーがまだ真のセットにあるかどうかを確認することです。(これにより、イベント中に追加された新しいオブザーバーがすべて忘れられますが、この場合は気にしません)

この問題に関する提案/解決策はありますか?

4

4 に答える 4

2

実際にはオブザーバー オブジェクト自体ではなく、ポインターまたは何かをコンテナーに格納していると仮定します。明らかに、オブザーバー コードでオブザーバー オブジェクトを削除することはできないからです。

std::set は、イテレータが生きている間にセットを変更する必要がある場合には使用できません。それはただの目的ではありません。

コンテナーから何かを削除する必要がある場合は、呼び出し元 (反復子を持つコード) にそのオブザーバーをコンテナーから削除するように指示するイベント ルーチンから値を返すことを試みることができます。そのコードは、反復子が指しているものを削除し、シーケンスに沿って適切に続行できます。

何かを追加する必要がある場合は、コンテナーに直接追加しないでください。代わりに、それらをキューに追加し、イベントを起動するコードに、コンテナーの反復処理が完了した後に新しいものを追加させます。

コンテナーに含まれるオブジェクトの数が少なく、オブジェクトが小さい (ポインター) 場合は、おそらくコンテナーをコピーして、そのコピーを反復処理します。そうすれば、オブザーバーは、イテレーターを台無しにすることなく、必要なだけコンテナーをいじることができます。

于 2012-10-10T16:57:24.733 に答える
1

オブザーバーの通知メソッドに、そのオブザーバーを指す反復子を受け入れさせ、次のオブザーバーを指す反復子を返します。

// PSEUDO-CODE not to be taken literally.

class normalObserver : public Observer {
  iterator notify(iterator me) { 
    assert(*me == this); 
    // do stuff
    return ++me;
  }
 };

 class deleteMeObserver : public Observer {
   iterator notify(iterator me) {
    assert(*me == this); 
     // do stuff
     object.observers.erase(me++);
     return me;
   }
 };

 class deleteEveryObserver :public Observer {
   iterator notifiy(iterator me) {
    assert(*me == this); 
     // do stuff
     object.observers.clear();
     return object.observers.end();
   }
};

 class Object {
   set::set<Observer*> observers;
   void notifyObservers() {
     for(it = observers.begin(); it != observers.end(); ) {
       it = (*it)->notify(it);
     }
   }
 };
于 2012-10-10T17:01:56.187 に答える
0

std::listの代わりに使用することをお勧めしsetます。この方法では、もちろんそれ自体を削除しない限り、要素を削除してもイテレータが無効にならないからです。

イテレータの代わりにオブザーバー オブジェクトにポインター (スマート ポインターまたはおそらく弱いポインター) を保持することで、それ自体を削除したいオブザーバーを処理できます (このように、それ自体が削除されても、ポインターは無効になりません (スマートである限り))。自分自身も削除しません))

于 2012-10-10T17:03:43.250 に答える
0

オブザーバーにセットへの直接アクセスを許可しないでください。set と iterator の両方にアクセスできるメソッドを介して変更を行うようにします。これは、オブザーバーが呼び出される前にインクリメントする必要があります。このメソッドは、反復子のアイテムが削除されているかどうかを確認し、削除されている場合はインクリメントできます。すべて削除の場合は、イテレータを に設定するだけend()です。

于 2012-10-10T17:23:46.430 に答える