Sto provando a scrivere una classe Subject
sicura dallo observer pattern. Voglio sapere se si utilizza weak_ptr
è il modo migliore per conservare IObserver
casi in modo tale che:Schema di osservatore utilizzando weak_ptr
- Non è possibile utilizzare un'istanza
IObserver
dopo che è stato free'd. - La classe
Subject
non è valida per i riferimentiIObserver
che dovrebbero essere liberati (lapsed listener problem). - La classe
Subject
deve essere thread-safe.
Sfortunatamente, i nostri standard di codifica dicono che non è consentito utilizzare boost. Credo di essere stato una persona cattiva in una vita precedente. Fortunatamente, sono autorizzato a utilizzare C++ 11 (ciò che viene fornito con Visual Studio 2012).
Ecco un esempio di classe Observer
.
// Observer interface that supports notify() method
class IObserver
{
public:
virtual void notify() const = 0;
virtual ~IObserver() {}
};
// Concrete observer implementation that prints a message
class Observer : public IObserver
{
public:
Observer(const std::string& message) : m_message(message){}
void notify() const {
printf("%s\r\n", m_message.c_str());
}
private:
std::string m_message;
};
E qui è la classe Subject
.
// Subject which registers observers and notifies them as needed.
class Subject
{
public:
// Use shared_ptr to guarantee the observer is valid right now
void registerObserver(const std::shared_ptr<IObserver>& o)
{
std::lock_guard<std::mutex> guard(m_observersMutex);
m_observers.push_back(o);
}
void unregisterObserver(const std::shared_ptr<IObserver>& o)
{
std::lock_guard<std::mutex> guard(m_observersMutex);
// Code to remove the observer from m_observersMutex
}
// This is a method that is run in its own thread that notifies observers of some event
void doNotify()
{
std::lock_guard<std::mutex> guard(m_observersMutex);
// Notify any valid observers of events.
std::for_each(m_observers.cbegin(), m_observers.cend(),
[](const std::weak_ptr<IObserver>& o)
{
auto observer = o.lock();
if (observer) {
observer->notify();
}
});
// Remove any dead observers. These are ones which have expired().
m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(),
[](const std::weak_ptr<IObserver>& o)
{
return o.expired();
}), m_observers.end());
}
private:
std::vector<std::weak_ptr<IObserver>> m_observers;
std::mutex m_observersMutex;
};
Ecco alcuni codice che esercita Subject
:
int main(int argc, wchar_t* argv[])
{
Subject subject;
auto observerHello = std::make_shared<Observer>("Hello world");
subject.registerObserver(observerHello);
{
// Create a scope to show unregistration.
auto observerBye = std::make_shared<Observer>("Good bye");
subject.registerObserver(observerBye);
subject.doNotify();
}
printf("%s\r\n", "Observer good bye is now be destructed");
subject.doNotify();
return 0;
}
È il mio utilizzo di weak_ptr
thread-safe? Da qui https://stackoverflow.com/a/2160422/1517648 I penso che sia.
Si tratta di un modo legittimo per risolvere il problema dell'ascolto scaduto?
non 2012 ha gamma base per i cicli? perché stai usando for_each? (irrilevante alla domanda, heh) – David
È solo un'abitudine, non c'è altra ragione se non quella a cui sono abituato. – Steve