Returning an Object

Returning an Object is pretty simple in C++, we merely set the return type to the class and then 'return' as usual. Except when it does this it returns a copy of what we gave it, not the original object.

It does have some desirable qualities, however. It allows one to easily refer to an object just once for use elsewhere in a program and it gets destructed after the expression it occurs in is evaluated, which is quite nice really and cuts down on some pointer usage (in the code, I'm aware this is still done with pointers).

What I really want, however, is to be able to build up the object in a function, and then return the same object that was on the stack in the function and have it destruct and popped off the stack after the expression the function was called from is evaluated. This is because often copying is expensive if this function is being called with frequency of a high order, and sometimes copying the object is inappropriate for some strange object classes.

The workaround, of course, is to create the object on the heap, return the pointer, and then manually 'delete' the object after use in the expression. But this requires the programmer to know that they must 'delete' the object after using its pointer, so not a very nice way of doing it. Also you have to use pointer notation, which I like avoiding most of the time especially if I am using a class with lots of operator overloads.


TL;DR How to return an object on the stack in a function and not a copy of the object?
Short answer is: you can't.

Of course, there are ways around that.

The workaround, of course, is to create the object on the heap, return the pointer, and then manually 'delete' the object after use in the expression. But this requires the programmer to know that they must 'delete' the object after using its pointer, so not a very nice way of doing it.

That's why one generally returns a smart pointer (such as std::unique_ptr) instead of a raw pointer. It will take care of deleting the referred object when the smart pointer goes out of scope.

The other way is to move the object into the return value (this happens automatically if you're returning a local object). This requires a move constructor.

You should also note that the compiler will often be able to apply RVO (return value optimization), resulting in no copy being made. However, it still requires a copy constructor to exist.

tl;dr: if a move constructor can be generated for your class, you're returning a local object and you're using C++11, then you're already done.
Last edited on
Thanks for the quick reply.

I'm not keen on using a smart pointer as I'm never keen making more data to work around language weaknesses!

EDIT: Now that I think about it, a std::unique_pointer wouldn't have much overhead.

However, I've never heard of a move constructor, that sounds very interesting! Also, you say that this occurs for local objects automatically? What is a local object, is that an object on the stack inside the block that the return statement is in? Does this mean that a copy is merely simulated for returning something on the stack?
Last edited on
Now that I think about it, a std::unique_pointer wouldn't have much overhead.

Their nature allows them not to have any sort of overhead (neither in run time, nor binary size nor memory usage). However, it's different for other smart pointer types, e.g. shared_ptr.

However, I've never heard of a move constructor, that sounds very interesting!

They were introduced in C++11. I recommend googling for "c++11 move semantics".

What is a local object, is that an object on the stack inside the block that the return statement is in?

Yes (or any block inside the function).

Does this mean that a copy is merely simulated for returning something on the stack?

Not really. Take this example:

1
2
3
4
5
6
7
8
9
10
vector<string> foo()
{
  vector<string> ret;
  return ret;
}

int main()
{
  vector<string> vec=foo();
}


Now one of several things can happen (3 being the most likely here):
1) the compiler is not able to perform copy/move elision. Then ret is moved into the return value and the return value is moved into vec.
2) the compiler is able to apply (N)RVO. Then ret is constructed directly into the return value of foo, which is then moved into vec.
3) the compiler is able to perform copy/move elision in both cases. Then ret in foo() is constructed directly into vec in main(). No copies or moves happen.
4) if vector<string> didn't have a move constructor (it does though, unless you're compiling in C++98 mode), then a copy would be performed instead of a move in the above cases.

Moves are intended to be very fast operations. In the case of a vector, it would move its internal pointer to the actual data when moved into another vector (well, technically it's the other vector that rips the pointer from the vector that is to be moved).

More stuff to read:
http://en.wikipedia.org/wiki/Copy_elision
http://en.wikipedia.org/wiki/C%2B%2B11#Rvalue_references_and_move_constructors
Last edited on
Topic archived. No new replies allowed.