C++11: Replace all non-owning raw pointers with st

2019-01-10 03:50发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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_ptrs instead of one unique_ptr and many raw-pointers in the general case and use unique_ptr if you really have a performance problem!