Sometimes I'm really sure that I want to have circular dependence of pointers, and every object on cycle should be able to use his pointer (so it can't be weak_ptr).
My question is: Does this mean that I have bad design?
What if I want to implement graph? Can I use smart pointers? In graphs there are cycles, but with weak_ptr I can't use "->". What can I do?
I read some articles, reference and topics on StackOverflow, but it looks like I still don't get smart pointers. Really, why doesn't exists some variant of weak_ptr with "->"?
You can use
weak_ptr
somewhere in the cycle; you just need to promote theweak_ptr
s toshared_ptr
s before you can dereference them. You can do this by callingweak_ptr::lock()
or simply by passing aweak_ptr
toshared_ptr
's constructor (but beware; this will throw abad_weak_ptr
exception if the object theweak_ptr
points to has been destroyed.If you really can't do this (for example, if all objects involved in the cycle are of the same type, which is probably the case in your graph example), another option is to put a
release
function somewhere in the chain that causes the object in question to set all itsshared_ptr
s to null.Approach this from the conceptual side, not the implementation one. Smart pointers represent ownership. And existence of smart pointers does not invalidate the role of raw pointers as non-owning observers.
Does each object have a single, clearly defined owner (e.g. a graph owns all of its vertices and edges)? If so, use
std::unique_ptr
to hold the vertices and edges in the graph, and use raw pointers inside vertices and edges to refer to each other.Is shared ownership applicable (e.g. a vertex only exists as long as at least one edge is connected to it)? If so, use
std::shared_ptr
to represent that ownership, again with raw pointers for non-owning observers. If you need mutual ownership (i.e. ownership cycles) where "a vertex only exists as long as an edge refers to it, and an edge only exists as long as a vertex refers to it," then 1. double-check that such design is correct and maintainable, and 2. if so, use astd::weak_ptr
somewhere in the cycle to break the ownership loop. You can alwayslock()
aweak_ptr
to obtain ashared_ptr
.For your particular graph scenario, I believe "everything's owned by the graph" would be the most logical ownership scheme; but that depends on the idiosyncracies of your task.
Yes, but it is a starting point.
Let's consider some of the smart pointers availabe to use.
unique_ptr
- a single owner exists that is responsible for disposing of the object.shared_ptr
- many (or potentially many) owners exist and the last one must dispose of the objectweak_ptr
- many owners may exist but this is not one of them, the weak pointer may out live the object pointed to, if the object pointed to is disposed of the weak pointer will be null (that is the lock method will return a null shared_ptr)observer_ptr
(n3840)- Not yet part of the standard so C-style pointers (T*) can be used instead if needed. These work very much like a weak_ptr, but it is the programmer’s responsibility to make sure that all observers are not dereferenced after the object pointed to is disposed of.A solution is to split the design into an object that will own all the pieces and the pieces (the cycle nodes). The owning object can use
shared_ptr
orunique_ptr
to automatically manage the life time of the nodes. The nodes themselves can refer to each other withweak_ptr
,observer_ptr
, or a Reference (Node&)