Consider the code bellow.
// Consider that MyObject receives three integers in it's constructor.
MyObject createObject()
{
MyObject result(1, 2, 3);
return result;
}
As long as I know, when you return an object in C++(like in this function), the compiler will create a new object(as a local variable) in the stack for the client. In this exemple, it will be a new MyObject object that will be created using the copy constructor with result as parameter. This implies in redundant processing for the machine: the object result will be created in the stack and then a second object will be created and returned.
An alternative to avoid this is creating the object dynamically(not in stack) and then return it's pointer. But it will cause memory leak or force the client to delete the object after use. So, the solution I found was to use an smart pointer, like this:
// Consider that MyObject receives three integers in it's constructor.
boost::smart_ptr<MyObject> createObject()
{
boost::smart_ptr<MyObject> result(new MyObject(1, 2, 3));
return result;
}
This works, but the smart pointer still is an object that will be recreated(even if the cost is low becouse, basically, it holds just a pointer). I was guessing if there's a way to do this a more easily; or if there wasn't a way already implemented by the compiler to optimize this work or something alike. Is there a syntax to say to the compiler to do what I want?
Profile your code first, I'm 99% sure there's no overhead from copying.
NRVO exists and will most probably kick in in this case.
If it weren't for copy elision, returning collections (like std::vector and the likes) would cause massive problems.
when you return an object in C++(like in this function), the compiler will create a new object(as a local variable) in the stack for the client.
Not really. It will probably be created directly in the calling context, precisely to prevent the extra copy.
If you are willing to use C++11, you can use "move semantics". Let me explain by first giving an example:
class expensive {
public:
expensive() : n(0), V(NULL) {}
expensive(int _n) {
n = _n;
V = new int[_n];
for (int i = 0; i < n; ++i) V[i] = i*i;
}
expensive(const expensive& rhs) { // copy constructor
n = rhs.n;
V = new int[n];
for (int i = 0; i < n; ++i) V[i] = rhs.V[i];
}
expensive(expensive&& rhs) { //move constructor
n = rhs.n;
V = rhs.V;
rhs.n = -1;
rhs.V = NULL;
printf("Moving\n");
}
~expensive() {
if (n == -1) printf("Destroying 'moved' instance\n");
if (V) delete [] V;
}
private:
int *V;
int n;
};
expensive f(int x) {
expensive temp(50);
expensive temp2(temp); // Copy temp to temp2, both are valid now
expensive temp3(std::move(temp)); // Move temp to temp3, temp is not valid anymore
return std::move(temp2); // move temp2 to the return
}
int main() {
expensive E = f(30);
return 0;
}
The output of this program is:
Moving
Moving
Destroying 'moved' instance
Destroying 'moved' instance
All the normal STL containers support move semantics. Also, std::swap uses it (and therefore, std::sort as well).
EDIT: As was noted, if NRVO is being used, the last std::move is unnecessary. Let me make a more complex example. This is a toy example, but it should show my point.
class expensive {
... // Same definition as before
}
expensive g(int x) {
vector<expensive> V;
V.reserve(2*x);
for (int i = 1; i <= x; ++i)
V.push_back(expensive(i)); // Here the objects are moved to the vector
for (int i = 1; i <= x; ++i)
V.emplace_back(i); // Here the objects are constructed directly in the vector
return std::move(V[x/2]); // V[x/2] is moved to the answer
}
int main() {
expensive x(g(2)); // 3 objects should be moved, 2 in push_back and one on the return
return 0;
}
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