0

I am fairly new to C++ and I know of three ways of returning a local variable and all have their downsides:

Person& getPerson()
{
   Person bob;
   return bob;
}

Clearly not a good idea.

Person getPerson()
{
   Person bob;
   return bob;
}

No chance of a null pointer or dangling reference but a performance hit.

Person* getPerson()
{
   return new Person();
}

No chance of a null pointer but surely this violates the basic rules of OO design. Another object will have to delete this - but why should it have to? The implemenation of the getPerson() method has nothing to do with it.

So, I am looking for an alternative. I have heard of shared pointers and smart pointers (standard and Boost) but I'm not sure whether any of them are designed to deal with this problem. What do you guys suggest?

Armada
  • 718
  • 8
  • 19

7 Answers7

6

Option #2: return by value.

Person getPerson()
{
  Person bob;
  return bob;
}

There is no performance hit here. This copy may be (and probably will be) elided by your compiler. In fact, even if you turn off your compiler's copy elision optimizations, with a C++11 compiler this will be considered as a move first.

In fact, even if you then do Person p = getPerson(), which would normally involve two copies, both may be elided.

See §12.9/31:

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

And §12.9/32:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • Very clever. So really, I can return by value even for massive vectors? The same wouldn't apply for multiple returns up the stack, or would it? :S – Armada Feb 26 '13 at 20:50
  • 2
    @Framo Returning a vector from a function will either be optimized away completely, or the vector will be *moved* instead of copied, which basically boils down to a handful of assignments. Don't worry about it! – fredoverflow Feb 26 '13 at 20:51
  • @Frammo ignoring C++11, then it depends. It might work, if the multiple returns all return the same object (see named return value optimization, NRVO). – juanchopanza Feb 26 '13 at 20:52
  • The people who made these compilers are smart! Thanks for your help guys. One more thing, where can I find this C++ standards document(s)? – Armada Feb 26 '13 at 20:55
  • 2
    You can buy the official standard from national standards bodies or ISO, but the latest draft is free: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3485.pdf, there's a link to it on the left sidebar at http://isocpp.org – Jonathan Wakely Feb 26 '13 at 20:57
  • 2
    @Frammo Jonathan answered your question already, but reading the standard made me a lot more knowledgeable about C++. It's definitely good to have it as a reference at all times. :) – Joseph Mansfield Feb 26 '13 at 21:16
4

No chance of a null pointer or dangling reference but a performance hit.

Actually, no performance hit at all. See for example here: Want Speed? Pass by Value.

Compiler can easily optimize that, with strategies called copy elision and the named return value optimization (check out the link for that).

ipc
  • 8,045
  • 29
  • 33
3

You shouldn't worry too much about a performance hit here:

Person getPerson()
{
   Person bob;
   return bob;
}

The copy you are worried about will most likely be elided in what is called return value optimization (RVO). The C++ standard allows compilers to make this optimization, even if it breaks the as-if rule. I haven't come across a compiler that wouldn't elide a copy in this kind of expression for a long time:

Person p = getPerson();

In C++11, even in the absence of copy elision, this would be a candidate for a move construction. This could be an extremely cheap operation, but that really depends on the type in question. In any case, copy elision is hard to avoid.

See this related post.

See this demo.

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • This was only an example. In my actual application there is an object responsible for object relational mapping to/from a database. It constructs an object from a database row and returns it. – Armada Feb 26 '13 at 20:53
  • 1
    @Frammo then it depends on the details of the function (and the type being returned, if you have move semantics). – juanchopanza Feb 26 '13 at 20:54
  • @Frammo I would expect it to be elided. See [this example](http://ideone.com/JnMVsR) with a similar structure to your code. – juanchopanza Feb 26 '13 at 21:15
2

As others have already pointed out, return value optimization helps minimize the performance hit from simply returning a value.

Move semantics (new with C++11) can also help in this regard -- a return expression is pretty much the canonical example of an "xvalue", which is eligible to have its value moved from the source to the destination, rather than copied. Especially for a type (e.g., vector) that mostly consists of a pointer to the real data, this can be extremely beneficial, as it allows essentially a shallow copy instead of a deep copy (i.e., instead of making a copy of the entire vector, it only ends up copying the pointer).

A shared_ptr or unique_ptr can work here as well. A shared_ptr is basically a reference counted pointer, so (pre-C++11) it lets you keep the object alive by just incrementing a reference count during the return process, then decrementing it again afterwards. At least in a single-threaded environment, this is generally pretty cheap -- often cheaper than making a copy of the data.

A unique_ptr does roughly similar things, but without the overhead of incrementing and decrementing a reference count. The basic difference is that instead of a making copying cheap, it moves the pointer to avoid doing a copy at all.

Any of these can work, but pretty clearly the best of them in most cases is to just return the value (and if it makes sense, add a move constructor and/or move assignment operator to the type you're working with).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

Local variables go out of scope - their lifetime ends - once the function execution is complete. Therefore, generally it is not a good idea to return references or pointers to local variables.

What you might want to do is to return references or pointers to class member variables, which maintain their lifetime as long as the class object is in scope or has a valid lifetime.

hichetu
  • 41
  • 2
0

Should you ever need to return polymorphic objects, I recommend using unique pointers:

std::unique_ptr<Person> getPerson()
{
    return std::unique_ptr<Person>(new Programmer);
}
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
0

I know I bang on about this a lot.

Another alternative is to not return anything.

Tell the object what to do:

  • display yourself, using this renderer
  • serialise yourself using this serialiser (implementation could be xml, database, json, network)
  • update your state for this time
  • decorate yourself with controls, using this control creator (creates sliders, dropdown lists, checkboxes, etc)

No need for getters on the whole. Make efforts to avoid them and you'll find your designs pleasantly changed, testable, reasonable.

Peter Wood
  • 23,859
  • 5
  • 60
  • 99