My approach is:
class SomeClass
{
std::vector<std::unique_ptr<MyObject>> myObjects;
public:
void takeOwnership(MyObject *nowItsReallyMyObject)
{
myObjects.emplace_back(std::move(nowItsReallyMyObject));
}
};
Am I doing everything correctly or are there any better solutions?
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.
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.
unique_ptr allows only one owner of the underlying pointer while shared_ptr is a reference-counted smart pointer. In this implementation, the developer doesn't need to explicitly delete the allocated memory towards the end of the function.
std::unique_ptr s are useful when you want to tie the lifetime of the object to a particular block of code, or if you embedded it as member data inside another object, the lifetime of that other object. The object exists until the containing block of code is exited, or until the containing object is itself destroyed.
The move
is redundant.
Myself, I'd do this:
void takeOwnership(std::unique_ptr<MyObject> nowItsReallyMyObject)
{
myObjects.emplace_back(std::move(nowItsReallyMyObject));
}
because I would want to move the unique_ptr
ownership semantics as far "out" as possible.
I might write this utility function:
template<class T>
std::unique_ptr<T> wrap_in_unique( T* t ) {
return std::unique_ptr<T>(t);
}
so callers can:
foo.takeOwnership(wrap_in_unique(some_ptr));
but even better, then can push the borders of unique_ptr
semantics out as far as they reasonably can.
I might even do:
template<class T>
std::unique_ptr<T> wrap_in_unique( T*&& t ) {
auto* tmp = t;
t = 0;
return std::unique_ptr<T>(tmp);
}
template<class T>
std::unique_ptr<T> wrap_in_unique( std::unique_ptr<T> t ) {
return std::move(t);
}
which lets callers transition their T*
into unique_ptr
s easier. All of their T*
->unique_ptr<T>
is now wrapped in a std::move
, and zeros the source pointer.
So if they had
struct I_am_legacy {
T* I_own_this = 0;
void GiveMyStuffTo( SomeClass& sc ) {
sc.takeOwnership( wrap_in_unique(std::move(I_own_this)) );
}
};
the code can be transformed into:
struct I_am_legacy {
std::unique_ptr<T> I_own_this;
void GiveMyStuffTo( SomeClass& sc ) {
sc.takeOwnership( wrap_in_unique(std::move(I_own_this)) );
}
};
and it still compiles and works the same. (Other interaction with I_own_this
may have to change, but part of it will already be unique_ptr compatible).
You should accept the unique_ptr
from the get-go:
class SomeClass
{
std::vector<std::unique_ptr<MyObject>> myObjects;
public:
// tells the world you 0wNz this object
void takeOwnership(std::unique_ptr<MyObject> myObject)
{
myObjects.push_back(std::move(myObject));
}
};
This way you make it clear you take ownership and you also help other programmers to avoid using raw pointers.
Further reading: CppCoreGuidelines R.32
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