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