views:

467

answers:

9

So I was writing some code, and I had something like this:

class Box
{
    private:
    float x, y, w, h;

    public:
    //...
    Rectangle & GetRect( void ) const
    {
        return Rectangle( x, y, w, h );
    }
};

Then later in some code:

Rectangle rect = theBox.GetRect();

Which worked in my debug build, but in release there were "issues" returning that Rectangle by reference -- I basically got an uninitialized rectangle. The Rectangle class has an = operator and a copy constructor. Without getting into why this broke, I'm actually more interested in the correct way to return a (new) object by reference for the purpose of assigning copying to a variable. Am I just being silly? Should it not be done? I know I can return a pointer and then dereference on assignment, but I'd rather not. Some part of me feels like returning by value would result in redundant copying of the object -- does the compiler figure that out and optimize it?

It seems like a trivial question. I feel almost embarrassed I don't know this after many years of C++ coding so hopefully someone can clear this up for me. :)

+12  A: 

No you cannot do this. Essentially what you're trying to do in this sample is return a reference to a temporary variable on the stack. By the time the reference is returned, the variable it's pointing to will be destroyed and hence the reference is invalid.

JaredPar
pbhogan
The problem is not the constness but rather that the thing that you have a reference to is created on the stack.
Stephen Doyle
@pbhogan, no. I shouldn't have added the const word to my answer. That was a mistake on my part. You should just return this by value here.
JaredPar
+10  A: 

You can't return a reference to a temporary object on the stack. You have three options:

  1. Return it by value
  2. Return by reference via a pointer to something that you created on the heap with the new operator.
  3. Return by reference what you received by reference as an argument. [EDIT: Thanks to @harshath.jr for pointing this out]

Note that when you return by value as in the code below, the compiler should optimize the assignment to avoid the copy - i.e. it will just create a single Rectangle (rect) by optimizing the create+assign+copy into a create. This only works when you create the new object when returning from the function.

Rectangle GetRect( void ) const
{
    return Rectangle( x, y, w, h );
}

Rectangle rect = theBox.GetRect();
Stephen Doyle
Once again, there is no assignment in this code.
anon
Option 3: return by reference what you recived as an arguement by reference. This is especially used while overriding operators.
Here Be Wolves
examples of pointer would've been good in this answer likeRectangle * GetRect(void) const{ return new Rectangle(x, y, w, h)}Rectangle* rect = theBox.GetRect();later delete rect;
AppDeveloper
+1  A: 
  • Either return a reference to the innards of your Box class (have a Rectangle member. Returning a const reference is advised).
  • or just return a Rectangle. Note that using the idiom return SomeClass(a,b,c); will probably trigger a return value optimization (RVO) on decent compiler.

Check your std::complex implementation for details.

You can call me Chuck
+1  A: 

You may be getting confused by the concept of the lifetime of a temporary. Consider:

void f1( const A & a ) {
}

A f2() {
   return A;
}

f1( f2() );

This is OK code, and the standard says that the nameless temporary that f2 creates must hang round long enough to be useable in f1.

However, your case is somewhat different. The thing your function returns is a reference, and therefore the nameless temporary is also a reference. That reference must hang round long enough to be useful, but the thing it refers to need not.

anon
You're right of course. I've been spending too much time in dynamic languages -- I started expecting the lifetime of the temporary to follow the reference.
pbhogan
+1  A: 

This is not possible. A reference is another form of a pointer and you in fact return an address of an object that will have been destroyed (destructor called ) and possibly even overwritten by the time the caller receives control.

You can either

  • call new and return a pointer (maybe you should think of a smart pointer) to a heap-allocated object or
  • return by value or
  • pass the object by reference into a function so it fills it.
sharptooth
+5  A: 

Returning an object by value (see example below) may actually be less expensive than you think. The compiler often optimizes out the extra copy. This is called the return value optimization.

    Rectangle GetRect( void ) const
    {
            return Rectangle( x, y, w, h );
    }
Cayle Spandon
+1 for naming the specific allowed optimisation
James Hopkin
A: 

If the rectangle bitwise looks like the Box i.e consists of four floats (but got different member functions) you could use a *reinterpret_cast*, although I would not at all recommend it:

    const Rectangle & GetRect( void ) const
    {
            assert(sizeof(Rectangle) == sizeof(Box));
            return reinterpret_cast <Rectangle> (*this);
    }
Viktor Sehr
A: 

we can use auto_ptr, if we want to use new and safe from Memory Leak

class Box  { 

  private:    float x, y, w, h;   

  public:    

  //...    

  std::auto_ptr<Rectangle> GetRect( void ) const   
  {        
      return std::auto_ptr<Rectangle> ( new Rectangle( x, y, w, h ));   
  }

};
Warrior
+2  A: 

Is there a right way to return a new object instance by reference in C++?

No, not by reference. There are two ways to create a new object:

On the stack:

Rectangle makeRect()
{
  return Rectangle(x, y, w, h);
}
Rectangle r = makeRect(); // return by value

On the heap:

Rectangle * makeRect()
{
  return new Rectangle(x, y, w, y);
}
Rectangle * r = makeRect(); // returned a pointer, don't forget to delete it later

Why not something like this?

class Box
{
  private:
    Rectangle mRectangle;

  public:
    Box(float x, float y, float w, float h) :
      mRectangle(x, y, w, h) // Forgive me for making assumptions
                             // about the inner workings of your
                             // code here.
    {
    }

    const Rectangle & GetRect() const
    {
      return mRectangle;
    }
};

Rectangle rect = theBox.GetRect();

The 'assignment' should work now. (Technically this is not an assignment operator, but a copy constructor being invoked.)

Hoping to help

StackedCrooked
A good summary of my options, thank you. I implement your last option whenever possible already, however my example here was somewhat simplistic. Box is actually something more complicated and GetRect returns a calculated Rectangle. Anyway, your first option is what I should be doing... I was just trying to be smarter than the compiler I guess :p
pbhogan