I am learning C++ from a background of mostly C and Java and I am curious on what is the best way to return an object in C++ without having to copy the object. From my understa
This question will probably be closed as a duplicate. It is an oft-asked question. However I would like to answer it anyway.
In C++11, when you are the client of an object like std::string
, you should pass it around by value and not worry so much about efficiency:
std::string getStr(){
return "Hello";
}
std::string hello = getStr();
At this level you do not need to be concerned with rvalue references. Just know that std::string
can "copy" from rvalues (such as the return from getStr()
) very efficiently. This "copy" is actually called a "move" and is enabled automatically because you are copying from an rvalue (an anonymous temporary).
Don't try to optimize copying by reverting to reference counting. Only use reference counting if you need shared ownership semantics. This statement isn't always true. But for learning the basics, it is close enough that it is a good rule of thumb to follow.
You need to start worrying about rvalue references when you design your class that needs to be passed around by value:
class Widget
{
// pointer to heap data
public:
// ...
};
For a brief introduction to rvalue references and move semantics, N2027 is a decent tutorial. N2027 is too long to be pasted in here, but short enough to be an easy read. std::string
follows the basics laid down in N2027 to enable you to pass it around by value guilt free. And you can follow those same design patterns in your Widget
.
If you want to use smart pointer in this case, maybe unique_ptr is a better choise.
Often you can avoid copies by returning a const reference to a member variable. For example:
class Circle {
Point center;
float radius;
public:
const Point & getCenter() const { return center; };
};
This is equivalent to return ¢er
in execution and in fact will likely be inlined simply resulting in direct (read-only) access of the member from the caller.
In cases where you construct a temporary to return the compiler may elide the extra copy as an optimization, requiring no special syntax on your part.
Rvalue refernces will be much faster, as they are an intrinsic language feature, rather than a reference counting class. Using a smart or shared pointer here will give no gain, and greatly decrease clarity. However, the rvalue references are a part of the language designed for this kind of thing. Use the rvalue reference.
By default you should simply pass things by value. If a function does not need to modify or copy a parameter then it can be taken by const&
, but otherwise just take parameters by value. Return objects by value and leave it to move semantics or RVO to avoid a copy.
C++ uses and encourages passing objects 'by-value'. Move semantics are a feature that allows code to do this while making fewer copies than before they were introduced. Typically code does not need to use rvalue reference types directly in order to gain the benefit of move semantics. This is because temporary objects are rvalues and overload resolution will automatically select methods that take rvalue references.
For example:
std::string getStr(){
return "Hello";
}
std::string hello = getStr();
This code uses move semantics. return "Hello"
constructs a string temporary and so the constructor used to initialize the return value object will be the move constructor (though actually the return value optimization will almost certainly avoid this move). Then the value returned by getStr()
is a temporary, so the constructor selected for string hello
is again the move constructor.
I think you can also do std::string &&hello = getStr();
, but it's probably not necessary since hello
will already be move constructed.
Another way I could think of is using a shared pointer
I don't think smart pointers are a generally a good idea unless you really need the specific ownership semantics they provide. Using straightforward 'by-value' passing is a good default and move semantics generally allow you to use it without extra copying.
Also is it recommended that fields in a class be rvalue references if they won't be set using the constructor?
No, member references are not usually a good idea. They make things more complicated and you aren't even allowed to use them as your example shows. If your example code were allowed then sh.str = getStr();
would be undefined behavior, because you're trying to assign to a non-existant object. It's like std::string *str; *str = getStr();
.