It seems like std::unique_ptr
solves a problem that can also be solved with move-semantics, i.e. transferring ownership of a uniquely-owned resource. Here are some examples where they seem to perform the same job:
class SomeResource { /* resourcey stuff */ };
void ProcessResourcePtr(std::unique_ptr<SomeResource> r) { /* do stuff */ }
void ProcessResourceRvalRef(SomeResource&& r) { /* do stuff */ }
class OwnerOfResourcePtr {
private:
std::unique_ptr<SomeResource> r_;
public:
OwnerOfResourcePtr(std::unique_ptr<SomeResource> r) : r_(std::move(r)) { }
};
class OwnerOfResourceRvalRef {
private:
SomeResource r_;
public:
OwnerOfResourceRvalRef(SomeResource&& r) : r_(std::move(r)) { }
};
int main()
{
// transfer ownership to function via unique_ptr
std::unique_ptr<SomeResource> r1(new SomeResource());
ProcessResourcePtr(std::move(r1));
// transfer ownership to function via rvalue ref
SomeResource r2;
ProcessResourceRvalRef(std::move(r2));
// transfer ownership to instance via unique_ptr
std::unique_ptr<SomeResource> r3(new SomeResource());
OwnerOfResourcePtr(std::move(r3));
// transfer ownership to instance via rvalue ref
SomeResource r4;
OwnerOfResourceRvalRef(std::move(r4));
return 0;
}
To my eye, these both solve almost exactly the same problem in slightly different ways. I'm not 100% clear on the advantages of one way versus the other. I know that pointer moves are probably faster than move constructors/assignments, although both are usually assumed to be quite efficient. I'm also aware that move semantics allow you to keep your data on the stack (see r2, r4) instead of requiring a heap allocation/deallocation with new/malloc/etc (see r1, r3), which I think is a good thing (is it?).
In general when should one prefer a unique_ptr
over move semantics, or vice-versa? Are there any use cases that can only be solved by one or the other?
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.
In C++11 we can transfer the ownership of an object to another unique_ptr using std::move() . After the ownership transfer, the smart pointer that ceded the ownership becomes null and get() returns nullptr.
Yes, you can compare it to nullptr after the move and it is guaranteed to compare equal. This is clearly true after calling release(). But std::move doesn't call release(). So how does the compiler know to restore the invariant of the unique_ptr?
It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };
If you have a class (perhaps written by someone else) which does not have a move constructor, perhaps not even a copy constructor, then you may have to use unique_ptr
.
If an instance is optional, i.e. may not be present, you should use unique_ptr
.
If you can only initialize an object after your constructor is called, and the object is not default-constructible, you must delay construction, and can use unique_ptr
for this.
Even if a class has a move constructor, that might be more expensive than moving a single pointer. For example, if the class contains dozens of POD instances, or an array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With