In the actual C++ standard, creating collections satisfying following rules is hard if not impossible:
- exception safety,
- cheap internal operations (in actual STL containers: the operations are copies),
- automatic memory management.
To satisfy (1), a collection can't store raw pointers. To satisfy (2), a collection must store raw pointers. To satisfy (3), a collection must store objects by value.
Conclusion: the three items conflict with each other.
Item (2) will not be satisfied when shared_ptr
s are used because when a collection will need to move an element, it will need to make two calls: to a constructor and to a destructor. No massive, memcpy()
-like copy/move operations are possible.
Am I correct that the described problem will be solved by unique_ptr
and std::move()
? Collections utilizing the tools will be able to satisfy all 3 conditions:
- When a collection will be deleted as a side effect of an exception, it will call
unique_ptr
's destructors. No memory leak. unique_ptr
does not need any extra space for reference counter; therefore its body should be exact the same size, as wrapped pointer,- I am not sure, but it looks like this allows to move groups of
unique_ptrs
by usingmemmove()
like operations (?), - even if it's not possible, the
std::move()
operator will allow to move eachunique_ptr
object without making the constructor/destructor pair calls.
unique_ptr
will have exclusive ownership of given memory. No accidental memory leaks will be possible.
Is this true? What are other advantages of using unique_ptr
?
I agree entirely. There's at last a natural way of handling heap allocated objects.
In answer to:
there was a proposal to allow this, but it hasn't made it into the C++11 Standard.
Yes, a container of
unique_ptr
will satisfy this.unique_ptr
's size is implementation-defined. While all reasonable implementations ofunique_ptr
using it's default destructor will likely only be a pointer in size, there is no guarantee of this in the standard.Absolutely not.
unique_ptr
is not a trivial class; therefore, it cannot bememmove
d around. Even if it were, you can't justmemmove
them, because the destructors for the originals need to be called. It would have to be amemmove
followed by amemset
.Also incorrect. Movement does not make constructors and destructors not be called. The
unique_ptr
's that are being destroyed need to be destroyed; that requires a call to their destructors. Similarly, the newunique_ptr
s need to have their constructors called; that's how an object's lifetime begins.There's no avoiding that; it's how C++ works.
However, that's not what you should be worried about. Honestly, if you're concerned about a simple constructor/destructor call, you're either in code that you should be hand-optimizing (and thus writing your own code for), or you're prematurely optimizing your code. What matters is not whether constructors/destructors are called; what matters is how fast the resulting code is.
Yes, it will.
Personally, I'd say you're doing one of the following:
Being excessively paranoid about copying objects. This is evidence by the fact that you consider putting a
shared_ptr
in a container is too costly of a copy. This is an all-too-common malady among C++ programmers. That's not to say that copying is always good or something, but obsessing over copying ashared_ptr
in a container is ridiculous outside of exceptional circumstances.Not aware of how to properly use move semantics. If your objects are expensive to copy but cheap to move... then move them into the container. There's no reason to have a pointer indirection when your objects already contain pointer indirections. Just use movement with the objects themselves, not
unique_ptr
s to objects.Disregarding the alternatives. Namely, Boost's pointer containers. They seem to have everything you want. They own pointers to their objects, but externally they have value semantics rather than pointer semantics. They're exception safe, and any copying happens with pointers. No
unique_ptr
constructor/destructor "overhead".It looks like the three conditions I've enumerated in my post are possible to obtain by using Boost Pointer Container Library.
Yes, you are right. I would only add this is possible thanks to r-value references.
This question illlustrates why I so love the Boehm garbage collector (libgc). There's never a need to copy anything for reasons of memory management, and indeed, ownership of memory no longer needs to be mentioned as part of APIs. You have to buy a little more RAM to get the same CPU performance, but you save hundreds of hours of programmers' time. You decide.