2016-09-15 9 views
5

を実装するためのweak_ptrを使用することです:私がこれまで持って何Observerパターン

Observer.h

class Observer 
{ 
public: 
    ~Observer(); 
    virtual void Notify() = 0; 
protected: 
    Observer(); 
}; 

class Observable 
{ 
public: 
    ~Observable(); 
    void Subscribe(std::shared_ptr<Observer> observer); 
    void Unsubscribe(std::shared_ptr<Observer> observer); 
    void Notify(); 
protected: 
    Observable(); 
private: 
    std::vector<std::weak_ptr<Observer>> observers; 
}; 

Observer.cpp

void Observable::Subscribe(std::shared_ptr<Observer> observer) 
{ 
    observers.push_back(observer); 
} 

void Observable::Unsubscribe(std::shared_ptr<Observer> observer) 
{ 
    ??? 
} 

void Observable::Notify() 
{ 
    for (auto wptr : observers) 
    { 
     if (!wptr.expired()) 
     { 
      auto observer = wptr.lock(); 
      observer->Notify(); 
     } 
    } 
} 

(デ/コンストラクタはここで実装されているが、空、私はそれらを残しました)

私が立ち往生しているのは、購読解除の手順を実装する方法です。私は、イレーズ・エキストラ・イディオムを見つけましたが、Observableをどのようにセットアップしたかで「すぐに使える」ことは理解できません。オブザーバーベクトルのweak_ptr要素を調べて、オブザーバーを削除できるようにするにはどうすればよいですか?

また、Un/Subscribeプロシージャのパラメータタイプについていくつかのアドバイスを探しています。 std::shared_ptr<Observer>&またはconst std::shared_ptr<Observer>&を使用する方が良いでしょうか?私たちはそれを変更しないでしょうか?

ObservablesにObservidersを所有させたくないのは、パターンの意図を裏切っているようですが、最終的にパターンを利用する残りのプロジェクトをどのように構造化したいのかは確かです。つまり、私が検討しているセキュリティ/オートメーションの追加層は、Observersにweak_ptrのミラーベクトルを保存させることです。その後、オブザーバーは、加入していたObservableからすべて退会することができ、Observableは退出時にObservableを観察している各オブザーバーからバックリファレンスを消去することができます。明らかに、2つのクラスはこのようなシナリオでは友人になるでしょう。

答えて

2

あなたはこのようstd::erasestd::remove_ifを使用することができます:あなたは確かにconst std::shared_ptr<Observer>&としてobserverを渡す必要があり

void Observable::Unsubscribe(std::shared_ptr<Observer> observer) 
{ 
    std::erase(
     std::remove_if(
      this->observers.begin(), 
      this->observers.end(), 
      [&](const std::weak_ptr<Observer>& wptr) 
      { 
       return wptr.expired() || wptr.lock() == observer; 
      } 
     ), 
     this->observers.end() 
    ); 
} 

+3

注意: 'std :: remove_if'は、要素をコンテナから削除しません。それと一緒に 'container.erase'を使う必要があります。削除イディオムを検索します。 – Nawaz

+0

...この場合、 'std :: remove_if'は(おそらく)非効率的です。 'std :: find_if'と' .erase'はより良く(あるいは少なくとも*意味的に*正しい)実行されます。 – Nawaz

+1

wptr.expired()を追加して安全性をチェックし、死んだオブザーバを削除します。 – M2tM

1

私が取り組んでいるのは、登録解除手順を実装する方法です。

オブザーバがコンテナの変更で無効にならないため、オブザーバをstd :: listに格納することをお勧めします。次にオブザーバーでsubscribeするとイテレーターが格納され、サブスクライブ中にイテレーターを使用してエレメントが削除されます。 もちろん、別の答えで示唆されているように、std :: vectorとstd :: remove_ifを使用することができます。

ここで、すべての* _ptrものについてです。 C++でRAIIはあなたの友人ですので、それを使用してください。公開解除方法を取り除く。代わりに、オブザーバーは自分自身をデストラクターに登録解除する必要があります。これにより、物事は非常に単純化されます。ウィークポインタをロックしなくても、オブザーバが削除されていれば、リストにはありません。マルチスレッドアプリケーションを使用している場合は、オブザーバーリストをミューテックスで保護することを忘れないでください。このデザインを使用する場合、Observableはオブザーバへのポインタのみを必要とし、オブザーバの格納方法は必要ありません。

class Observer { 
public: 
    void subscribe(std::function<void()> unsubscribe) { 
     unsubscribe_ = std::move(unsubscribe); 
    } 

    virtual ~Observer() { 
     unsubscribe_(); 
    } 
private: 
    std::function<void()> unsubscribe_; 
}; 

class Observable { 
public: 
    void subscribe(Observer* observer) { 
     std::lock_guard<std::mutex> lock(observablesMutex_); 
     auto itr = observers_.insert(observers_.end(), observer); 
     observer->subscribe([this, itr]{ 
      std::lock_guard<std::mutex> lock(observablesMutex_); 
      observers_.erase(itr); 
     }); 
    } 

private: 
    std::list<Observer*> observers_; 
    std::mutex observablesMutex_; 
}; 

注:このコードでは、Observableの前に必ずオブザーバーを破棄する必要があります。


更新:あなたはC++ラムダにもっと慣れる場合はSTD ::機能を有する観察者が特別なクラス階層を持つより多くの場合より便利であるとことがあります。この場合、APIは次のようになります:

class Handle { 
public: 
    explicit Handle(std::function<void()> onDestroy) 
     : onDestroy_(std::move(onDestroy)) {} 

    Handle(const Handle&) = delete; 

    Handle(Handle&&) = default; 

    virtual ~Observer() { 
     onDestroy_(); 
    } 
private: 
    std::function<void()> onDestroy_; 
}; 

class Observable { 
public: 
    Handle subscribe(std::function<void()> observer) { 
     std::lock_guard<std::mutex> lock(observablesMutex_); 
     auto itr = observers_.insert(observers_.end(), observer); 
     return {[this, itr]{ 
      std::lock_guard<std::mutex> lock(observablesMutex_); 
      observers_.erase(itr); 
     }}; 
    } 

private: 
    std::list<std::function<void()>> observers_; 
    std::mutex observablesMutex_; 
}; 
関連する問題