Item 31: Never return a reference to a local object or to a dereferenced pointer initialized by new
within the function.
This Item may sound complicated, but it's not. It's simple common sense. Really. Honest. Trust me.
Consider first the matter of returning a reference to a local object. The problem here is that local objects are just that, local. That means they're constructed when they're defined, and they're destructed when they go out of scope. Their scope, however, is that of the function body in which they're located. When the function returns, control leaves its scope, so the objects local to that function are automatically destructed. As a result, if you return a reference to a local object, that local object has been destructed before the caller of the function ever gets its computational hands on
This problem usually raises its ugly head when you try to improve the efficiency of a function by returning its result by reference instead of by value. The following example is the same as the one in Item 23, which pursues in detail the question of when you can return a reference and when you
class Rational { // class for rational numbers public: Rational(int numerator = 0, int denominator = 1); ~Rational();
...
private: int n, d; // numerator and denominator
// notice that operator* (incorrectly) returns a reference friend const Rational& operator*(const Rational& lhs, const Rational& rhs); };
// an incorrect implementation of operator* inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); return result; }
Here, the local object result
is constructed upon entry into the body of operator*
. However, local objects are automatically destroyed when they go out of scope. result
will go out of scope after execution of the return
statement, so when you write
Rational two = 2;
Rational four = two * two; // same as // operator*(two, two)what happens during the function call is
result
is constructed.
result
, and this reference is squirreled away as operator*
's return value.
result
is destroyed, and the space it used to occupy on the stack is made available for use by other parts of the program or by other programs.
four
is initialized using the reference of step 2.
Everything is fine until step 4, at which point there occurs, as they say in the highest of high-tech circles, "a major lossage." The reference initialized in step 2 ceased to refer to a valid object as of the end of step 3, so the outcome of the initialization of object four
is completely
The lesson should be clear: don't return a reference to a local
"Okay," you say, "the problem is that the object I want to use goes out of scope too soon. I can fix that. I'll just call new
instead of using a local object." Like
// another incorrect implementation of operator* inline const Rational& operator*(const Rational& lhs, const Rational& rhs) { // create a new object on the heap Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
// return it return *result; }
This approach does indeed avoid the problem of the previous example, but it introduces a new one in its place. To avoid a memory leak in your software, you know you must ensure that delete
is applied to every pointer conjured up by new
, but ay, there's the rub: who's to make the matching call to delete
for this function's use of new
?
Clearly, the caller of operator*
must see to it that delete
is applied. Clear, yes, and even easy to document, but nonetheless the cause is hopeless. There are two reasons for this pessimistic
First, it's well-known that programmers, as a breed, are sloppy. That doesn't mean that you're sloppy or that I'm sloppy, but rare is the programmer who doesn't work with someone who is shall we say? a little on the flaky side. What are the odds that such programmers and we all know that they exist will remember that whenever they call operator*
, they must take the address of the result and then use delete
on it? That is, they must use operator*
like
const Rational& four = two * two; // get dereferenced // pointer; store it in // a reference ...
delete &four; // retrieve pointer // and delete it
The odds are vanishingly small. Remember, if only a single caller of operator*
fails to follow the rules, you have a memory
Returning dereferenced pointers has a second, more serious, problem, because it persists even in the presence of the most conscientious of programmers. Often, the result of operator*
is a temporary intermediate value, an object that exists only for the purposes of evaluating a larger expression. For
Rational one(1), two(2), three(3), four(4); Rational product;
product = one * two * three * four;
Evaluation of the expression to be assigned to product
requires three separate calls to operator*
, a fact that becomes more evident when you rewrite the expression in its equivalent functional
product = operator*(operator*(operator*(one, two), three), four);
You know that each of the calls to operator*
returns an object that needs to be deleted, but there is no possibility of applying delete
, because none of the returned objects has been saved
The only solution to this difficulty is to ask clients to code like
const Rational& temp1 = one * two; const Rational& temp2 = temp1 * three; const Rational& temp3 = temp2 * four;
delete &temp1; delete &temp2; delete &temp3;
Do that, and the best you can hope for is that people will ignore you. More realistically, you'd be skinned alive, or possibly sentenced to ten years hard labor writing microcode for waffle irons and toaster
Learn your lesson now, then: writing a function that returns a dereferenced pointer is a memory leak just waiting to
By the way, if you think you've come up with a way to avoid the undefined behavior inherent in returning a reference to a local object and the memory leak haunting the return of a reference to a heap-allocated object, turn to Item 23 and read why returning a reference to a local static
object also fails to work correctly. It may save you the trouble of seeking medical care for the arm you're likely to strain trying to pat yourself on the