This is a topic that has always consufed me, especially since I really cut my teeth on OOP with Java.

In Java, you really don't think much about making object copies. I remember at first learning the intracacies of .clone(), only to to discover months later that my code contained zero usages of it. If you use your objects properly, you just don't need to have copies of them floating around.

Anyway, lets take the CPlusPlusStarterClass? and add a new method, scale() which creates an array of scaled for the object for which it is called.

IntArray IntArray::scale(int factor)
{  IntArray temp(*this);           // make a copy
   for (int i = 0; i < size; i++)  // scale the data
   {   temp[i] *= factor;
   }
   return temp;
}

I've proven to myself that this works. But it doesn't work quite the way I thought it would. Yes, a copy is made when I declare temp. But it seems like that copy, temp, should die when I go out of scope and return, and that another needs to be made for the caller. Luckily, that does not happen. The destructor for temp is not called in member function, but is by its caller.

Maybe the local is supposed to be trashed and a copy made, but modern compilers (even with -O0, I might add) eliminate the extra copy. That's a relief, because it eliminates the savings you might get from doing this...

IntArray* IntArray::scale_ptr(int factor)
{  IntArray* temp = new IntArray(*this);  // make a copy
   for (int i = 0; i < size; i++)
   {   (*temp)[i] *= factor;
   }
   return temp;
}

Not only would the caller have to clean up this returned object, but if this function was, say, a multiply operator you could have a guaranteed memory leak (see Scott Meyers' Effective C++ Lessons 23 & 31) if you did x = a*b*c - the a*b kicks out an anonymous object with nothing to delete even if you remembered to do so.

Now there's a third case, so very subtley different. What if we return by reference...

IntArray& IntArray::scale_ref(int factor)
{  IntArray* temp = new IntArray(*this);  // make a copy
   for (int i = 0; i < size; i++)
   {   (*temp)[i] *= factor;
   }
   return temp;
}

Remember, a reference is just another name for something else. So you're saying you want to return temp, not a copy of it. This will compile but generates a warning on both gcc and icc, and rightly so, because indeed temp will be destroyed at the end of the member function.

So what's strange? Well, in fact the first 'return by value' example IS, as an optimization, returning the local, not a copy of it. So at a low level, the compiler strangely with the 'return by reference' case goes out of its way to delete a local! Makes sense, of course, to comply with the C++ language.

-- MattWalsh - 07 May 2005

Topic revision: r1 - 07 May 2005 - 21:31:00 - MattWalsh
 
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback