Reputation: 22448
C++ references are still confusing to me. Suppose I have a function/method which creates an object of type Foo
and returns it by reference. (I assume that if I want to return the object, it cannot be a local variable allocated on the stack, so I must allocate it on the heap with new
):
Foo& makeFoo() {
...
Foo* f = new Foo;
...
return *f;
}
When I want to store the object created in a local variable of another function, should the type be Foo
void useFoo() {
Foo f = makeFoo();
f.doSomething();
}
or Foo&
?
void useFoo() {
Foo& f = makeFoo();
f.doSomething();
}
Since both is correct syntax: Is there a significant difference between the two variants?
Upvotes: 3
Views: 1680
Reputation: 1535
Your first code does a lot of work:
void useFoo() {
Foo f = makeFoo(); // line 2
f.doSomething();
}
Thinking of line 2, some interesting things happen. First, the compiler will emit code to construct a Foo
object at f
using the default constructor of the class. Then, it will call makeFoo()
, which also creates a new Foo
object and returns a reference to that object. The compiler will also have to emit code that copies the temporary return value of makeFoo()
into the object at f
, and then it will destroy the temporary object. Once line 2 is done, f.doSomething()
is called. But just before useFoo()
returns, we destroy the object at f
, as well, since it is going out of scope.
Your second code example is much more efficient, but it's actually probably wrong:
void useFoo() {
Foo& f = makeFoo(); // line 2
f.doSomething();
}
Thinking of line 2 in that example, we realize that we don't create an object for f
since it is just a reference. The makeFoo()
function returns an object that it has newly allocated, and we keep a reference to it. We call doSomething()
through that reference. But when the useFoo()
function returns, we don't ever destroy the new object that makeFoo()
created for us and it leaks.
There's a few different ways to fix this. You could just use the reference mechanism you have in your first code fragment, if you don't mind the extra constructors, creation, copying and destruction. (If you have trivial constructors and destructors, and not much (or none) state to copy, then it doesn't matter much.) You could just return a pointer, which has the strong implication that the caller is responsible for managing the life cycle of the referenced object.
If you return a pointer, you've implied that the caller must manage the life cycle of the object, but you haven't enforced it. Someone, someday, somewhere will get it wrong. So you might consider making a wrapper class that manages the reference and provides accessors to encapsulate the management of the objects. (You could even bake that into the Foo
class itself, if you wanted to.) A wrapper class of this type is called a "smart pointer" in its generic form. If you're using the STL, you'll find a smart pointer implementation in the std::unique_ptr
template class.
Upvotes: 3
Reputation: 8831
A function should never return a reference to a new object that gets created. When you are making a new value, you should return a value or a pointer. Returning a value is almost always preferred, since almost any compiler will use RVO/NRVO to get rid of the extra copy.
Returning a value:
Foo makeFoo(){
Foo f;
// do something
return f;
}
// Using it
Foo f = makeFoo();
Returning a pointer:
Foo* makeFoo(){
std::unique_ptr<Foo> p(new Foo()); // use a smart pointer for exception-safety
// do something
return p.release();
}
// Using it
Foo* foo1 = makeFoo(); // Can do this
std::unique_ptr<Foo> foo2(makeFoo()); // This is better
Upvotes: 2
Reputation: 258618
Yes, the first one will make a copy of the returned reference, while the second will be a reference to the return of makeFoo
.
Note that using the first version will result in a memory leak (most likely), unless you do some dark magic inside the copy constructor.
Well, the second will result in a leak as well unless you call delete &f;
.
Bottom line: don't. Just follow the crowd and return by value. Or a smart pointer.
Upvotes: 5