问题
I'm trying to rewrite our Observer / Observable implementation to use std::shared_ptr/std::weak_ptr to get rid of some nasty race conditions currently present in the code.
Typically, the observers registers themselves when some condition is met or when they are constructing child objects like so:
// Used to be raw 'this' now child instead derives a weak_ptr and stores it
child->addObserver(shared_from_this())
And unregisters themselves in destructor like so:
child->removeObserver(this); // Not shared_from_this() since in destructor
In some situations this works fine, however in many instances the observer wants to register itself when in constructor. Since the shared_ptr hasn't been created yet we cannot call shared_from_this().
Since the weak_ptr is commonly recomended to implement the observer pattern in C++ I'm wondering what the idiomatic way to solve the above problem is.
Some thoughts:
- Let factory that creates the observer object register the observer. This leaks abstractions from the observer (why should the factory know who the child wants to observe?) and forces observer to expose internal objects that it may want to observe
- Add an init method that gets called by factory after constructor is complete, better than above but what is the semantic difference between a constructor and init anyway? What should be done where? Is it even RAII? Indeed, some languages even call their constructors init.
- Pass a lambda to constructor that takes another lambda that gets called after construction
- Some template magic maybe?
- Implement the observer pattern in some other way.
回答1:
One way to deal with the problem as you have asked it is to create an explicit observer object instance held by a shared_ptr and contained in the "parent". The observer object would dispatch observations to the parent.
However, since the child is registering a shared_ptr to a weak_ptr, there is actually no need for the parent to remove itself as an observer explicitly. When the child is sending out notifications to observers, it checks if the weak_ptr is valid first. If it is no longer valid, it can remove the observer in situ instead of notifying.
void notify_observers (Event e) const {
auto o = observers_.begin();
auto erase = [this](decltype(o) o) {
return observers_.erase(o);
};
while (o != observers_.end()) {
if (auto l = o->lock()) ++o, l->notify(e);
else o = locked_call(erase, o);
}
}
Try it online!
来源:https://stackoverflow.com/questions/53727439/registering-weak-ptr-observer-in-constructor