Professional C__ - Marc Gregoire [135]
C++ programmers do not often use stand-alone reference variables or reference data members. The most common use of references is for parameters to functions and methods. Recall that the default parameter-passing semantics are pass-by-value: Functions receive copies of their arguments. When those parameters are modified, the original arguments remain unchanged. References allow you to specify alternative pass-by-reference semantics for arguments passed to the function. When you use reference parameters, the function receives references to the function arguments. If those references are modified, the changes are reflected in the original argument variables. For example, here is a simple swap function to swap the values of two ints:
void swap(int& first, int& second)
{
int temp = first;
first = second;
second = temp;
}
Code snippet from References\ReferenceParameters.cpp
You can call it like this:
int x = 5, y = 6;
swap(x, y);
Code snippet from References\ReferenceParameters.cpp
When the function swap() is called with the arguments x and y, the first parameter is initialized to refer to x, and the second parameter is initialized to refer to y. When swap() modifies first and second, x and y are actually changed.
Just as you can’t initialize normal reference variables with constants, you can’t pass constants as arguments to functions that employ pass-by-reference:
swap(3, 4); // DOES NOT COMPILE
Code snippet from References\ReferenceParameters.cpp
Using rvalue references from C++11, it is possible to pass constants as arguments to functions that employ pass-by-rvalue-reference. Rvalue references are discussed later in this chapter.
References from Pointers
A common quandary arises when you have a pointer to something that you need to pass to a function or method that takes a reference. You can “convert” a pointer to a reference in this case simply by dereferencing the pointer. This action gives you the value to which the pointer points, which the compiler then uses to initialize the reference parameter. For example, you can call swap() like this:
int x = 5, y = 6;
int *xp = &x, *yp = &y;
swap(*xp, *yp);
Code snippet from References\ReferenceParameters.cpp
Pass-By-Reference versus Pass-By-Value
Pass-by-reference is required when you want to modify the parameter and see those changes reflected in the variable argument to the function or method. However, you should not limit your use of pass-by-reference to only those cases. Pass-by-reference avoids copying the arguments to the function, providing two additional benefits in some cases:
1. Efficiency: Large objects and structs could take a long time to copy. Pass-by-reference passes only a pointer to the object or struct into the function.
2. Correctness: Not all objects allow pass-by-value. Even those that do allow it might not support deep copying correctly. As Chapter 7 explains, objects with dynamically allocated memory must provide a custom copy constructor in order to support deep copying.
If you want to leverage these benefits, but do not want to allow the original objects to be modified, you can mark the parameters const, giving you pass-by-const-reference. This topic is covered in detail later in this chapter.
These benefits to pass-by-reference imply that you should use pass-by-value only for simple built-in types like int and double for which you don’t need to modify the arguments. Use pass-by-reference in all other cases.
Reference Return Values
You can also return a reference from a function or method. The main reason to do so is efficiency. Instead of returning a whole object, return a reference to the object to avoid copying it unnecessarily. Of course, you can only use this technique if the object in question will continue to exist following the function termination.
From a function or method, never return a reference to a variable that is locally scoped to that function or method, such as an automatically allocated variable on the stack that will be destroyed when the function ends.
A second reason to return