What I have so far is:
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();
}
}
}
(de/constructors are implemented here but empty, so I've left them out)
What I'm stuck on is how to implement the Unsubscribe procedure. I came across the erase - remove - end idiom, but I understand that it will not work "out of the box" with how I have setup my Observable. How do I inspect the weak_ptr elements in the observers vector such that I can remove the desired Observer?
I'm also looking for some advice on what the parameter type should be for my Un/Subscribe procedures. Would it be better to use std::shared_ptr<Observer>&
or const std::shared_ptr<Observer>&
, since we will not be modifying it?
I really do not want to have Observables owning their Observers, as it seems to betray the intentions of the pattern, and is certainly not how I want to structure the rest of the project that will ultimately be making use of the pattern. That said, an added layer of security / automation that I am considering is to have Observers store a mirror vector of weak_ptr. An Observer on its way out could then unsubscribe from all Observables it had subscribed to, and an Observable on its way out could erase the back-reference to itself from each of the Observers observing it. Evidently the two classes would be friends in such a scenario.
To implement weak_ptr , the "counter" object stores two different counters: The "use count" is the number of shared_ptr instances pointing to the object. The "weak count" is the number of weak_ptr instances pointing to the object, plus one if the "use count" is still > 0.
By using a weak_ptr , you can create a shared_ptr that joins to an existing set of related instances, but only if the underlying memory resource is still valid. A weak_ptr itself does not participate in the reference counting, and therefore, it cannot prevent the reference count from going to zero.
std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.
You can implement weak_ptr which works correctly with unique_ptr but only on the same thread - lock method will be unnecessary in this case.
You can use std::remove_if
with std::erase
like this:
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()
);
}
You should indeed pass observer
as const std::shared_ptr<Observer>&
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With