Passing and Returning References to Objects

There is some overhead involved in calling the copy constructor every time an object is passed by value to a function. However, you can duplicate the effect of passing the parameter by value, while avoiding the expense of the constructor call, by passing a reference to a constant object. For example:

void consume( const String &parm )

{

// Use the parm object

}

void main()

{

String myString( "here's my string" );

consume( myString );

}

The copy constructor is not called when a parameter is passed this way, because a new object is not being constructed. Instead, a reference is initialized with the object being passed. The compiler performs the equivalent of the following:

// Hypothetical initialization of reference parameter

const String &parm = myString; // Initialize reference

As a result, the function uses the same object as the caller.

Notice that the const keyword is used. Because a reference to a constant is passed, the function cannot modify the parameter, so the caller is guaranteed that the object remains safe. Only const member functions (that is, read-only member functions) can be invoked on the object.

Note that the copy constructor itself takes a reference to an object, rather than an object, as its parameter. If the copy constructor took an object itself as a parameter, it would have to call itself in order to initialize the parameter. This would cause an infinite recursion.

Returning a reference from a function can also be more efficient than returning an object. Recall the example of the operator= function:

String &String::operator=( const String &other )

{

//...

return *this;

}

void main()

{

String myString( "here's my string" );

String yourString, herString;

herString = yourString = myString;

}

The copy constructor is not called when the function returns, because a temporary object is not being created: only a temporary reference is created. When the herString object receives the value of the yourString = myString assignment statement, the compiler performs the equivalent of the following:

// Hypothetical initialization of reference return value

String &tempRef = yourString; // Initialize reference

// NOTE: yourString == *this

herString = tempRef; // Assignment of temp reference

// Equivalent to herString = yourString

However, you must use caution when returning a reference to objects or variables other than *this. The rules for returning references are similar to those for returning pointers. You cannot return a pointer to an automatic variable. For example:

// BAD TECHNIQUE: returning pointer to automatic variable

int *emitPtr()

{

int i;

return &i;

}

The integer i is an automatic variable, so it goes out of scope at the end of the function. That means the function returns a pointer to an integer which no longer exists. It is unsafe for the calling program to use such a pointer.

The same restriction applies to references:

// BAD TECHNIQUE: returning reference to automatic variable

int &emitRef()

{

int i;

return i;

}

It is safe, however, to return a reference or a pointer to a variable that has been dynamically allocated. Dynamically allocated objects remain in existence until they are deallocated, so references and pointers to them remain valid even after the function has exited. You can also safely return references or pointers to static or global variables.