Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Was the raw-pointer constructor of shared_ptr a mistake?

In hindsight, given make_shared, would shared_ptr have a constructor that takes a raw pointer had it been introduced with C++11?

Are there strong arguments or use cases in favor of this constructor?

It would have avoided the well documented pitfall of exception-safety and the memory allocation/performance advantage of using make_shared.

I believe another advantage of requiring shared_ptr construction via make_shared would be that it could be a single pointer under the hood, lowering its memory use and making things like atomic_compare_exchange a lot simpler (and possibly more efficient). (see presentation from C++Now)

I understand that a shared_ptr that basically is an intrusive_ptr (with the object and the control block coalesced) would lack features the current std::shared_ptr has. Like:

  1. the ability to free the object separately from the control block (which is nice if you have long lived weak_ptrs)

  2. compatibility with libraries that hand you raw pointers and the responsibility to free them

  3. the ability to hold arbitrary resources with custom deleters (or no deleter, for non-owning pointers)

  4. the ability to point to a sub-object (e.g., a member) while keeping the parent object alive.

What I'm suggesting is that these features may not be used commonly enough (or in the case of using it as a RAII-wrapper) may not be the best fit, to warrant the extra cost:

  1. a separate pointer to the control block
  2. (potentially) more complex atomic_compare_exchange logic, may not be worth it.

In a C++98 world (where shared_ptr was introduced) make_shared is less practical and less user friendly (the lack of perfect forwarding requires reference wrappers and the lack of variadic templates makes the implementation clunky).

like image 420
Arvid Avatar asked May 22 '16 17:05

Arvid


People also ask

What happens when you create two shared_ptr from the same raw pointer?

Therefore, when you create a std::shared_ptr from another one, it will increment the count properly (the two std::shared_ptr s point to the same struct). If you create two std::shared_ptr from the same raw pointer, although they actually point to the same resource, they have no way of knowing it!

What is the difference between raw pointer overload and shared_ptr overload?

The raw pointer overloads assume ownership of the pointed-to object. Therefore, constructing a shared_ptr using the raw pointer overload for an object that is already managed by a shared_ptr, such as by shared_ptr(ptr.get()) is likely to lead to undefined behavior, even if the object is of a type derived from std::enable_shared_from_this .

Does the shared_ptr take ownership of the parent pointer?

No, the shared_ptr takes ownership of the pointer and deletes it when there are no references anymore. See: Sorry I provided some wrong information . What if parent is a class member as follows:

How does a shared_ptr keep track of its reference count?

The reason why it can keep track is because std::shared_ptr has a pointer to a struct that stores the reference count besides the pointer to the actual object. Therefore, when you create a std::shared_ptr from another one, it will increment the count properly (the two std::shared_ptr s point to the same struct).


1 Answers

In hindsight, given make_shared, would shared_ptr have a constructor that takes a raw pointer had it been introduced with C++11?

What if you don't control the allocation of the object? What if you need to use a custom deleter? What if you need list-initialization instead of parens?

None of these cases is handled by make_shared.

Additionally, if you're using weak_ptr, a shared_ptr allocated via make_shared won't free any memory until all the weak_ptrs are destroyed as well. So even if you have a normal shared pointer where none of the above apply, it's possible that you may still prefer the raw pointer constructor.

Yet another situation would be if your type provides overloads for operator new and operator delete. These may make it ill-suited for make_shared, since those overloads will not be called - and presumably they exist for a reason.

like image 153
Barry Avatar answered Sep 18 '22 22:09

Barry