I have an implementation for Observer design pattern where Observer
stores a list of Subject
and vice versa. So in case a Observer/Subject goes out of scope, the corresponding object is deleted in the destructor and no dangling reference remains stored.
Thing is raw pointers are rarely used in modern C++ and I'd want to replace them with a smart pointer.
My understanding is the utility shouldn't be taking ownership of the objects so std::weak_ptr
makes more sense to me. Does that sound fair?
If so, changing the underlying data structure to hold weak_ptr
of Subject
would make sense, yes?
std::unordered_set<std::weak_ptr<Subject<T>>, std::hash<std::weak_ptr<Subject<T>>> _subjects;
I am unsure of whether there may be some corner cases where storing weak_ptr
in an std::unordered_set
could cause problems.
Running into compile errors related to the hash but below is the link to the sample code with weak_ptr.
Code with weak_ptr (though it doesn't compile)
Original sample code with raw pointer (shown below)
template<typename T>
class Subject;
template<typename T>
class Observer
{
std::set<Subject<T>*> _subjects; // TODO: change to smart pointer
public:
virtual void update(T val) = 0;
void addSubject(Subject<T>* subject)
{
printf ("[Observer] Adding Subject to observer - ");
_subjects.insert(subject);
std::cout << "number of Subjects = " << _subjects.size() << "\n\n";
}
void removeSubject(Subject<T>* subject)
{
printf ("[Observer] Removing Subject from observer - ");
_subjects.erase(subject);
std::cout << "number of Subjects = " << _subjects.size() << "\n";
}
size_t getNumberOfSubjects()
{
return _subjects.size();
}
void doesExist(Subject<T>* sub)
{
auto it = _subjects.find(sub);
std::cout << "[Observer] Subject Exists = " << (it != _subjects.end()) << std::endl;
}
virtual ~Observer()
{
while (!_subjects.empty())
{
(*_subjects.begin())->detach(this);
}
}
};
template<typename T>
class Subject
{
std::set<Observer<T>*> _observers; // TODO: change to smart pointer
protected:
Subject() = default;
public:
void attach(Observer<T>* observer)
{
printf ("~~ [Subject] Attaching observer ~~\n");
_observers.insert(observer);
observer->addSubject(this);
}
void detach(Observer<T>* observer)
{
printf ("[Subject] Detaching observer ~ ");
std::cout << "observers size = " << _observers.size() << "\n";
_observers.erase(observer);
observer->removeSubject(this);
}
void notify(const T& val)
{
for (auto& observer : _observers)
{
observer->update(val);
}
}
size_t getNumberOfObservers()
{
return _observers.size();
}
virtual ~Subject()
{
printf ("\n~Subject\n");
printf ("Observer size = %ld\n", _observers.size());
for (auto& obs : _observers)
{
obs->removeSubject(this);
}
}
};
template<typename T>
class ConcreteSubject : public Subject<T>
{
T _value;
public:
void set(const T& value)
{
_value = value;
this->notify(value);
}
};
template<typename T>
class ConcreteObserver : public Observer<T>
{
public:
void update(T value)
{
std::cout << "Observer Notified: " << value << "\n";
}
};
int main()
{
ConcreteObserver<int> obs;
ConcreteSubject<int> sub;
sub.attach(&obs);
sub.set(5);
printf ("\n~~> Before detaching:\nnumber of subjects in Observer = %ld ~~\n", obs.getNumberOfSubjects());
printf ("number of observers in Subject = %ld\n\n", sub.getNumberOfObservers());
sub.detach(&obs);
printf ("\n~~> After detaching:\nnumber of subjects in Observer = %ld ~~\n", obs.getNumberOfSubjects());
printf ("number of observers in Subject = %ld\n", sub.getNumberOfObservers());
// subject no longer exists in observer
obs.doesExist(&sub);
return 0;
}
Aucun commentaire:
Enregistrer un commentaire