What I'm wondering is, how does returning by value a Cat
actually differ from returning an std::unique_ptr<Cat>
in terms of passing them around, memory management and using them in practice.
Memory management wise, aren't they the same? As both a returned by value object and an object wrapped in a unique_ptr will have their destructors triggered once they go out of scope?
So, how would you compare both pieces of code:
Cat catFactory(string catName) {
return Cat(catName);
}
std::unique_ptr<Cat> catFactory(string catName) {
return std::unique_ptr(new Cat(catName));
}
If a function returns a std::unique_ptr<> , that means the caller takes ownership of the returned object. class Base { ... }; class Derived : public Base { ... }; // Foo takes ownership of |base|, and the caller takes ownership of the returned // object.
In theory, you should use unique_ptr for all pointers unless you know you want to share it, in which case you should use shared_ptr . The reason is that unique_ptr has less overhead since it doesn't count references.
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.
A unique pointer is an object that owns another object and manages that other object through a pointer. The unique pointer has exclusive ownership of the object it points to. This means unique_ptr does not share its pointer with any other unique_ptr. It cannot be copied to another unique_ptr.
Returning by value should be considered the default. (*) Deviating from the default practice, by returning std::unique_ptr<Cat>
, should require justification.
There are three main reasons to return a pointer:
Polymorphism. This is the best reason to return std::unique_ptr<Cat>
instead of Cat
: that you might actually be creating an object of a type derived from Cat
. If you need this polymorphism, you absolutely need to return a pointer of some sort. This is why factory functions usually return pointers.
Cat
cannot be moved cheaply or cannot be moved at all. "Inherently" unmovable types are rare; you should usually try to fix Cat
by making it cheaply movable. But of course Cat
could be a type owned by someone else, to which you cannot add a move constructor (or perhaps even a copy constructor). In that case, there is not much you can do other than use unique_ptr
(and complain to the owner).
The function has the potential to fail and be unable to construct any valid Cat
. In that case, one possibility is return by value anyway but throw an exception if the Cat
cannot be constructed; the other, in C++11/C++14, is to make the function return std::unique_ptr<Cat>
and have it return a null pointer when no Cat
can be constructed. In C++17, however, you should start returning std::optional<Cat>
instead of std::unique_ptr<Cat>
in that case, to avoid unnecessary heap allocation.
(*) This also applies to passing objects when the function being called needs its own copy of the value, e.g., a constructor that will initialize a class member from one of its arguments. Accept the object by value and move.
By default, return by value.
Exceptions to this rule:
unique_ptr
that's returned, but rather a shared_ptr
.I disagree with @Brian's answer regarding two of the exceptions he suggests:
nullptr
. Failing to return a valid value is what exceptions are for, and even if you want to avoid them - I'd suggest returning an std::optional
.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