Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm trying to wrap my head around the new idioms for C++11.

It seems that with shared_ptr at least, there is a substantive difference between using new T() and make_shared<T>().

But what of resetting a shared pointer to point to a new instance of something. Previously, I would typically use reset(new T()) member. However, doesn't this suffer from the same problem as not using make_shared() in the first place? (i.e. it doesn't allow make_shared to allocate the object, therefore it is forced to place the ref count in a separate allocation instead of in the same allocation as the T itself?)

Is it simply better going forward to use:

   mysharedptr = make_shared<T>(args...);

Or is there a better way?

And shouldn't reset offer variadic forwarding of arguments as make_shared does, so that one could write mysharedptr.reset(args...);?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
415 views
Welcome To Ask or Share your Answers For Others

1 Answer

There is indeed a substantial difference between:

shared_ptr<T> sp(new T());

And:

shared_ptr<T> sp = make_shared<T>();

The first version performs an allocation for the T object, then performs a separate allocation to create the reference counter. The second version performs one single allocation for both the object and the reference counter, placing them in a contiguous region of memory, resulting in less memory overhead.

Also, some implementations are able to perform further space optimizations in the case of make_shared<> (see the "We Know Where You Live" optimization done by MS's implementation).

However, that is not the only reason why make_shared<> exists. The version based on explicit new T() is not exception-safe in some situations, especially when invoking a function that accepts a shared_ptr.

void f(shared_ptr<T> sp1, shared_ptr<T> sp2);

...

f(shared_ptr<T>(new T()), shared_ptr<T>(new T()))

Here, the compiler could evaluate the first new T() expression, then evaluate the second new T() expression, then construct the corresponding shared_ptr<> objects. But what if the second allocation causes an exception before the first allocated object is bound to its shared_ptr<>? It will be leaked. With make_shared<>(), this is not possible:

f(make_shared<T>(), make_shared<T>())

Because allocated objects are bound to the respective shared_ptr<> objects inside each function call to make_shared<>(), this call is exception-safe. This is yet another reason why naked new should never be used unless you really know what you are doing. (*)

Considering your remark about reset(), you are right in observing that reset(new T()) will perform separate allocations for the counter and the object, just as the construction of a new shared_ptr<> will perform a separate allocation when a raw pointer is passed as an argument. Therefore, an assignment using make_shared<> is preferable (or even a statement such as reset(make_shared<T>())).

Whether or not reset() should support a variadic argument list, this is probably more of a kind of open discussion for which StackOverflow is not a good fit.

(*) There are a few situations that still require it. For instance, the fact that the C++ Standard Library lacks a corresponding make_unique<> function for unique_ptr, so you'll have to write one yourself. Another situation is when you do not want the object and the counter to be allocated on a single memory block, because the presence of weak pointers to the object will prevent the entire block from being deallocated, even though no more owning pointers to the object exist.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...