With the advent of std::unique_ptr
, the blemished std::auto_ptr
can finally be put to rest. So for the last several days, I have been changing my code to use smart pointers and to eliminate all delete
from my code.
Although valgrind says my code is memory-clean, the semantic richness of smart pointers will make for cleaner and easier-to-understand code.
In most of the code, the translation is simple: use std::unique_ptr
for in place of the raw pointers held by the owning objects, throw out delete
, and carefully sprinkle get()
, reset()
and move()
calls, as needed, to interface well with the rest of the code.
I am at the point where I am translating non-owning raw pointers to smart pointers now.
Since I was careful with the lifetimes of my objects (I ensure my modules only depend in one direction), valgrind tells me that I don't have any uninitialized reads, dangling pointers, or leaks. So, technically, I could just leave those non-owning raw pointers alone now.
However, one option is to change those non-owning raw pointers to std::shared_ptr
because I know they are acyclic. Or, would it be better to leave them as raw pointers?
I need some advice from veteran users of smart pointers as to what rules of thumb you use to decide whether to keep non-owning raw pointers as-is, or to translate them into std::shared_ptr
, keeping in mind that I constantly unit-test and valgrind my code.
EDIT: I might be misunderstanding the use of std::shared_ptr
- can they be used in conjunction with std::unique_ptr
, or is it the case that if I use std::shared_ptr
, all handles should also be std::shared_ptr
?
Personally, this is how I (more or less) do it:
- unique_ptrs are for sole ownership
- raw pointers mean whoever gave me the raw pointer guarantees the lifetime of that object to match or exceed my lifetime.
- shared_ptrs are for shared ownership
- weak_ptrs are for when a system wants to check if the object still exists before using it. This is rare in my code since I find it cleaner to have a system guarantee the lifetime of anything it passes it's subsystems (in which case I use a raw pointer)
By far I use more unique_ptrs than shared_ptrs, and more raw pointers than weak pointers.
Use a shared_ptr
when you require multiple things own a resource (and those owning things may go in and out of scope at "random"), use a unique_ptr
when a single thing owns the resource, and use a raw pointer when you just need to refer to it and not own it (and expect this referral to not last longer than the resource exists).
There is a fourth type, a sort of raw-pointer-for-shared_ptr
's, called weak_ptr
. You use that to refer to a shared_ptr
without actually owning it; you can then check if the object is still there and use it.
The only non-owning smart-pointer in the standard library is std::weak_ptr
. However, to use it, the actual owning object needs to hold the pointee in a std::shared_ptr
.
I assume you used std::unique_ptr
on those before. If you convert them to shared_ptr
now, you'll have the benefit that your non-owning pointers can know that the owning pointer lost is reference while raw pointers can be left dangling without any chance for the non-owning component to detect this. However, shared_ptr
will incur a (very?) small performance and memory overhead over unique_ptr
.
Personally, I recommend using one shared_ptr
and many weak_ptr
s instead of one unique_ptr
and many raw-pointers in the general case and use unique_ptr
if you really have a performance problem!