Reputation: 14869
I've gotten myself into a pickle: I'm coding a tree graph structure, where each layer contains a vector of pointers to previous layers (super types) and pointers to following (sub types) layers.
As you can imagine, this is a circular reference:
[last] -(super)-> [current]
[current] -(sub)-> [last]
When I allocate a new layer
I make sure I move it to current
and when I re-assign last to current, again I move it.
However, this doesn't solve the leak, in fact the only solution I've found is using raw pointers.
Here's a minified example omitting large parts of the code:
struct layer
{
std::unordered_set<std::string> words;
std::vector<layer*> sub_classes;
std::vector<layer*> super_classes;
}
The actual iteration is of wordnet senses
which populate a tree graph.
The function that does the hypernyms
(e.g., super-types of a word) is:
void iterate_sense(
Synset * sense,
std::shared_ptr<layer> last,
graph & rhs,
int lexical
)
{
std::shared_ptr<layer> current;
while (sense && last)
{
current = std::move(get_layer(sense));
last->super_classes.push_back(current.get());
current->sub_classes.push_back(last.get());
if (sense->ptrlist)
{
iterate_sense(sense->nextss,
last,
rhs, lexical);
}
last = std::move(current);
sense = sense->ptrlist;
}
}
Those pointers are owned by another class graph
.
When I use in layer
std::vector<std::shared_ptr<layer>> sub_classes;
std::vector<std::shared_ptr<layer>> super_classes;
Valgrind reports lost bytes.
What is the correct or elegant way of solving this? I assume that using raw pointers is not the best solution?
Am I correct to assume that the moment the owner graph
is lost,
the raw pointers will become invalid?
This isn't much of a problem because layers
themselves reside only in graphs.
I've read around SO and google that the correct usage is via combination of std::weak_ptr
and std::shared_ptr
however I don't understand how a weak_ptr would be used in such a case.
Upvotes: 0
Views: 56
Reputation: 420
Smart pointers, are about owner ships. It says who is responsible for a certain Object. Semantic is basically split as follows:
std::unique_ptr<>
: one function/object is the owner and responsible of the object.std::shared_ptr<>
: several function/objects are the owner and responsible of the object.raw pointer
: someone else is owner and responsible of the object.In your case I would recommend following semantic: A Layer is the owner of its child layers. A Child is not the owner of its parent.
Following this I'd use
std::vector<std::unique_ptr<layer>> sub_classes;
std::vector<layer*> super_classes;e
For iterate_sense
I'd suggest to use following signature:
void iterate_sense(
Synset * sense,
layer* last,
graph & rhs,
int lexical
)
This function is not fully or partially owning the object in variable last
. It only is granted limited access to it.
Not sure in which way graph
is owning these pointers. If graph
is actually the owner with std::unique_ptr<>
you can use only raw pointers
in layers
. This is exactly the correct way to use it.
In some cases it is possible to use std::weak_ptr<> to break circular references. I can not recommend using them for this. If you have a clean design of your data structures, it usually is very easy to say who is responsible/owner of a object. There are not that many cases where it is not clear. In these unclear cases, where more then an object is management/owned by several other objects you need std::shared_ptr<>. Under these circumstances you might need access to an object of which you don't know if it still exist. In this case you might want to use std::weak_ptr<>.
Upvotes: 2