I am currently designing an API and I am not sure whether my functions should take shared_ptr
or weak_ptr
. There are widgets that contain viewers. The viewers have a function add_painter
which adds a painter to the viewer. When a viewer needs to redraw, it uses its painters to draw into a buffer and displays the result. I came to the conclusion that the viewers should hold the painters using weak_ptr
:
remove_painter
function.There may be different kind of viewers, so they are hidden behind an interface. What signature is best for the add_painter
function in the interface?
Should I directly use void add_painter(weak_ptr<Painter> const& p)
? This implies that the concrete implentations store the painters using weak_ptr
, but I cannot enforce this: An implementation could just do painters.push_back(weak_ptr.lock())
and store a shared_ptr
.
Should I use void add_painter(shared_ptr<Painter> const& p)
instead? This implies that the viewers hold strong references, so that deleting a painter does not necessarily remove it from the viewer.
I also considered storing the painters directly in the interface class, but then it is no real interface anymore, is it?
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.
The only difference between weak_ptr and shared_ptr is that the weak_ptr allows the reference counter object to be kept after the actual object was freed. As a result, if you keep a lot of shared_ptr in a std::set the actual objects will occupy a lot of memory if they are big enough.
shared_ptr is also helpful in C++ Standard Library containers when you're using algorithms that copy elements. You can wrap elements in a shared_ptr , and then copy it into other containers with the understanding that the underlying memory is valid as long as you need it, and no longer.
Note that the control block used by std::weak_ptr and std::shared_ptr is thread-safe: different non-atomic std::weak_ptr objects can be accessed using mutable operations, such as operator= or reset , simultaneously by multiple threads, even when these instances are copies or otherwise share the same control block ...
You should not try to mitigate the Observer pattern with smart pointers and definitely you should avoid a situation when a client (View) can harass the server by converting the weak pointer to a shared pointer and storing it indefinitely barring it from being released by the server.
You should really consider the classic Observer pattern here requesting View to provide a painter_destroyed
callback function. It may be an annoyance but also gives the client an opportunity to implement some additional actions once the painter is destroyed. Otherwise finding that the painter exists no more just when one wants to use it may be quite irritating and affect overall program performance.
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