Reputation: 175
I have two std containers. Both of them with pointers to the same data structure. The first containing all the data and the second one containing only some of the same data. Should I use shared_ptr
or weak_ptr
on the second container?
Firstly, when I read the reference I thought about using unique_ptr
on the first collection. My first collection contains all the data and it's the only one that "owns". Meaning that if the data is not there it should be deleted. But then when I try to create the second collection I didn't know what to do. I created a unique pointer but now I need another pointer to the same element destroying uniqueness, but in fact the real owner was not the new pointer. So I understood (I hope I'm not wrong) that the uniqueness is on the way of reaching elements and not (e.g.) the possibility of delete it. So, shared_ptr
. I have them on my first collection. But now the second one comes up, I thought to use shared_ptr
here too. The ways to access the same data could be both, so the owners are two. But in my case the data is always deleted from the second before. And if I use a weak_ptr
, the number of owners will not increase. In both cases the element will be deleted when the first collection wants. At the end I'm using shared_ptr
because with weak_ptr
I need to lock()
every pointer in every line of code, making it less readable. But what should I really use?
Upvotes: 9
Views: 1689
Reputation: 17704
You haven't given a critical piece of information, which is whether you can guarantee that the two collections have the same lifetime or not. If you can guarantee that both collections have the same lifetime, then using unique_ptr for the collection that owns everything and raw pointers for the other (as @Galik suggests) is ideal.
If you cannot guarantee that the two lifetimes match, then whether you select shared_ptr's for both or shared_ptr for the first and weak for the second depends on when you want the objects to be destroyed. It sounds like only the first collection is a true owner, so you'd want weak pointers.
However, I'd heavily recommend that you stick with the first approach. It's much cleaner to avoid shared_ptr and weak_ptr. The danger is that if your two collections have different lifetimes, the first collection can be destroyed before the second (just a single misplaced brace), and then when the second collection tries to access, it has dangling pointers. You can of course simply be careful with your variables, but guaranteeing that two independent local variables consistently have the same lifetime is surprisingly easy to mess up.
If you make both collections elements of the same class, you guarantee that they get constructed and destructed at the same time. The exact details of what that class should look like and which code should go where depends on the details of your problem, of course. But even something as simple (albeit bizarre) as making them both the only members (and public) of the same struct is better than using two local variables.
Upvotes: 5
Reputation: 48615
It doesn't sound like you need std::shared_ptr
because your data is owned in one place.
I would recommend using std::unique_ptr
in the owning container and then simply put the raw pointers in the second and subsequent containers.
This works because you will never delete the raw pointers but the data they point to is still managed by a smart pointer and so will be released when you no longer need it.
Despite some bad press, raw pointers are perfectly respectable when used as non owning accessors to data that is owned by some other entity that will delete it at the appropriate time.
Upvotes: 11
Reputation: 6775
Good question,
I find by experience that using shared_ptr
/weak_ptr
, and even shared_ptr
at all by extension, is often resulting in some kind of a big blurred design.
Some people have advocated in the past that shared_ptr
could be considered harmful, period. Because it makes ownership floating. And that ownership should be clear by design. I'm not sure if I want to appropriate myself this advice and advocate it too; but for sure I'll repeat it here for the sake of answering the question.
Also, one can consider that using shared_ptr
everywhere is just the same as using garbage collection. It's just reference counting, but it behaves the same in the end. Only the performance is less good, it has been demonstrated in the past, a good garbage collection engine is faster than everything ref counted. (this is surely because of CPU atomic instructions and barriers needed in shared_ptr
, but I speculate.)
Maybe you should consider moving to a truely well garbage collected language/platform, such as C# on .NET 4 ?
Otherwise, how about moving away from the direct pointers scheme and use identifiers, you can make a manager pattern, with your 2 indexing data structures private in this manager. And the clients only ever see and work with identifiers (int
? uint64_t
? at your discretion) through the API of the manager.
The problem I find with this approach is the need to repeat the whole API of your manipulated objects in the manager. This is painful and doesn't respect DRY.
Otherwise, have you considered that maybe your data structures are not inherently necessary ?
What I mean is that, when you store pointers anyway, the indexing data structur tends to not be so large. It is only N*sizeof(ptr_t)
and not N*sizeof(value_t)
. Which suddenly makes std::vector
an excellent candidate in all circumstances. I mean the vector is already the best data structure for almost all usages, there are many advocates of this theory.
If your vector only holds pointer, do yourself a favor and alleviate the shared_ptr
overhead by using a boost::ptr_vector
instead.
I hope I brought some perspective.
Upvotes: 2
Reputation: 185852
What do you mean by "every line of code"? The usual pattern is as follows:
if (auto p = wp.lock()) {
// p is a shared_ptr; use it as often as you need within the block.
}
If you have an owning container and a referencing container that works correctly if the owner destroys the referee, then shared_ptr
/weak_ptr
is the way to go.
Upvotes: 5