Reputation: 9027
In modern C++ how should I manage unowned pointers?
I was thinking something like a weak_ptr
for unique_ptr
, but that doesn't seem to exist.
For instance, if I have a class A
that owns a pointer, I should use unique_ptr<X>
instead of an old X*
pointer, e.g.
class A
{
std::unique_ptr<X> _myX;
};
But then if I have another class B
that uses this pointer, what do I do here? With C style pointers I would do this:
class B
{
X* _someX;
};
That seems correct, but it isn't obvious from the code that I've referenced another object's pointer (for instance a reader may think I could have just not used a smart pointer).
std::shared_ptr<X>
- seems like a waste of reference counting because A
is guaranteed to outlive B
.std::weak_ptr<X>
- only works with shared_ptr
X&
- only works if X& is available in B's constructorThis seems like an obvious issue and sorry if it's been asked before. I've looked around and I have seen this question, but unfortunately the OP asked "is it OK to use X*" in one specific case. I'm looking for what I should generally be doing instead of X*, (if anything!).
Upvotes: 6
Views: 2589
Reputation: 63912
Bjarne Stroustrup has weighed in on this matter.
Pointers are really good at pointing to "things" and T* is a really good notation for that ... What pointers are not good at is representing ownership
He makes two suggestions:
T*
to mean "non-owning pointer to T
" in modern C++template<typename T> using observer_ptr = T*;
if you desire your variable declarations to explicitly document this non-ownership.Upvotes: 13
Reputation: 11
https://abseil.io/tips/116 is a good discussion of the options here. If you have the thing at construction time, and never need to repoint it, then T&
is probably good. If you don't, then "hey this might be null" is part of reality for that pointer, and T*
is fine - a "raw" pointer in modern C++ generally communicates an optional, non-owning reference.
Upvotes: 1
Reputation: 238411
That seems correct, but it isn't obvious from the code that I've referenced another object's pointer
Ideally, it should be obvious. If the pointer owned the resource, then it should have been a smart pointer. Since it isn't a smart pointer, that implies that it shouldn't own the resource.
That said, in the reality that has C / ancient C++ / badly designed interfaces which use owning pointers, you can clarify the lack of ownership like this:
class B
{
X* _someX; // no ownership
};
It is also possible to define a custom class template wrapper for this purpose, and there has been a proposal to include such template in the standard library, which was adopted as a technical specification, but has not been adopted into the standard proper. To my understanding, there's no consensus on whether such wrapper is useful or unnecessary.
Upvotes: 5
Reputation: 122994
Your question is largely opinion based, but we can take a look at what the core guideline has to say about it:
R.3: A raw pointer (a T*) is non-owning
Reason
There is nothing (in the C++ standard or in most code) to say otherwise and most raw pointers are non-owning. We want owning pointers identified so that we can reliably and efficiently delete the objects pointed to by owning pointers.
The guideline promotes to never use a raw pointer when it is an owning pointer. Once you do that, a raw pointer also readily signals that it is non-owning. The downside of this interpretation is:
Exception
A major class of exception is legacy code, especially code that must remain compilable as C or interface with C and C-style C++ through ABIs. The fact that there are billions of lines of code that violate this rule against owning T*s cannot be ignored. [...]
Not all raw pointers are non-owning, and often we cannot do much about that. However, if your code is written after C++11, then using a raw pointer should be sufficient to signal that it is not an owning pointer.
Upvotes: 2