|

When
pointers are not initialized, or when they are deleted,
they ought to be assigned to null (0). This is not
true for references. In fact, a reference cannot
be null, and a program with a reference to a null
object is considered an invalid program. When a
program is invalid, just about anything can happen.
It can appear to work, or it can erase all the files
on your disk. Both are possible outcomes of an invalid
program.
Most
compilers will support a null object without much
complaint, crashing only if you try to use the object
in some way. Taking advantage of this, however,
is still not a good idea. When you move your program
to another machine or compiler, mysterious bugs
may develop if you have null objects.
Passing
Function Arguments by Reference
In
Unit 5, "Functions," you learned that functions
have two limitations: Arguments are passed by value,
and the return statement can return only one value.
Passing
values to a function by reference can overcome both
of these limitations. In C++, passing by reference
is accomplished in two ways: using pointers and
using references. The syntax is different, but the
net effect is the same. Rather than a copy being
created within the scope of the function, the actual
original object is passed into the function.
Passing
an object by reference allows the function to change
the object being referred to.
Recall
that Listing 5.5 in Unit 5 demonstrated that a call
to the swap() function did not affect the values
in the calling function. Listing 5.5 is reproduced
here as Listing 9.5, for your convenience.
Listing
9.5. Demonstrating passing by value.
1:
//Listing 9.5 Demonstrates passing by value
2:
3:
#include <iostream.h>
4:
5:
void swap(int x, int y);
6:
7:
int main()
8:
{
9:
int x = 5, y = 10;
10:
11:
cout << "Main. Before swap, x: " <<
x << " y: " << y << "\n";
12:
swap(x,y);
13:
cout << "Main. After swap, x: " << x
<< " y: " << y << "\n";
14:
return 0;
15:
}
16:
17:
void swap (int x, int y)
18:
{
19:
int temp;
20:
21:
cout << "Swap. Before swap, x: " <<
x << " y: " << y << "\n";
22:
23:
temp = x;
24:
x = y;
25:
y = temp;
26:
27:
cout << "Swap. After swap, x: " << x
<< " y: " << y << "\n";
28:
29:
}
OUTPUT
Main. Before swap, x: 5 y: 10
Swap. Before swap, x: 5 y: 10
Swap. After swap, x: 10 y: 5
Main. After swap, x: 5 y: 10
ANALYSIS This program initializes two
variables in main() and then passes them to the
swap() function, which appears to swap them. When
they are examined again in main(), they are unchanged!
The problem here is that x and y are being passed
to swap() by value. That is, local copies were made
in the function. What you want is to pass x and
y by reference.
There
are two ways to solve this problem in C++: You can
make the parameters of swap() pointers to the original
values, or you can pass in references to the original
values.
Making
swap() Work with Pointers
When
you pass in a pointer, you pass in the address of
the object, and thus the function can manipulate
the value at that address. To make swap() change
the actual values using pointers, the function,
swap(), should be declared to accept two int pointers.
Then, by dereferencing the pointers, the values
of x and y will, in fact, be swapped. Listing 9.6
demonstrates this idea.
Listing
9.6. Passing by reference using pointers.
1:
//Listing 9.6 Demonstrates passing by reference
2:
3:
#include <iostream.h>
4:
5:
void swap(int *x, int *y);
6:
7:
int main()
8:
{
9:
int x = 5, y = 10;
10:
11:
cout << "Main. Before swap, x: " <<
x << " y: " << y << "\n";
12:
swap(&x,&y);
13:
cout << "Main. After swap, x: " << x
<< " y: " << y << "\n";
14:
return 0;
15:
}
16
17:
void swap (int *px, int *py)
18:
{
19:
int temp;
20:
21:
cout << "Swap. Before swap, *px: " <<
*px << " *py: " << *py << "\n";
22:
23:
temp = *px;
24:
*px = *py;
25:
*py = temp;
26:
27: cout << "Swap.
After swap, *px: " << *px << " *py:
" << *py << "\n";
28:
29:
}
OUTPUT
Main. Before swap, x: 5 y: 10
Swap. Before swap, *px: 5 *py: 10
Swap. After swap, *px: 10 *py: 5
Main. After swap, x: 10 y: 5
ANALYSIS Success! On line 5, the prototype
of swap() is changed to indicate that its two parameters
will be pointers to int rather than int variables.
When swap() is called on line 12, the addresses
of x and y are passed as the arguments. On line
19, a local variable, temp, is declared in the swap()
function. Temp need not be a pointer; it will just
hold the value of *px (that is, the value of x in
the calling function) for the life of the function.
After the function returns, temp will no longer
be needed.
On
line 23, temp is assigned the value at px. On line
24, the value at px is assigned to the value at
py. On line 25, the value stashed in temp (that
is, the original value at px) is put into py.
The
net effect of this is that the values in the calling
function, whose address was passed to swap(), are,
in fact, swapped.
Implementing
swap() with References
The
preceding program works, but the syntax of the swap()
function is cumbersome in two ways. First, the repeated
need to dereference the pointers within the swap()
function makes it error-prone and hard to read.
Second, the need to pass the address of the variables
in the calling function makes the inner workings
of swap() overly apparent to its users.
It
is a goal of C++ to prevent the user of a function
from worrying about how it works. Passing by pointers
takes the burden off of the called function, and
puts it where it belongs—on the calling function.
Listing 9.7 rewrites the swap() function, using
references.
Listing
9.7. swap() rewritten with references.
1:
//Listing 9.7 Demonstrates passing by reference
2:
// using references!
3:
4:
#include <iostream.h>
5:
6:
void swap(int &x, int &y);
7:
8:
int main()
9:
{
10:
int x = 5, y = 10;
11:
12:
cout << "Main. Before swap, x: " <<
x << " y: " << y << "\n";
13:
swap(x,y);
14:
cout << "Main. After swap, x: " << x
<< " y: " << y << "\n";
15:
return 0;
16:
}
17:
18:
void swap (int &rx, int &ry)
19:
{
20:
int temp;
21:
22:
cout << "Swap. Before swap, rx: " <<
rx << " ry: " << ry << "\n";
23:
24:
temp = rx;
25:
rx = ry;
26:
ry = temp;
27:
28:
cout << "Swap. After swap, rx: " <<
rx << " ry: " << ry << "\n";
29:
30:
}
OUTPUT
Main. Before swap, x:5 y: 10
Swap. Before swap, rx:5 ry:10
Swap. After swap, rx:10 ry:5
Main. After swap, x:10, y:5
ANALYSIS Just as in the example with
pointers, two variables are declared on line 10
and their values are printed on line 12. On line
13, the function swap() is called, but note that
x and y, not their addresses, are passed. The calling
function simply passes the variables.
When swap() is called, program execution jumps to
line 18, where the variables are identified as references.
Their values are printed on line 22, but note that
no special operators are required. These are aliases
for the original values, and can be used as such.
On
lines 24-26, the values are swapped, and then they’re
printed on line 28. Program execution jumps back
to the calling function, and on line 14, the values
are printed in main(). Because the parameters to
swap() are declared to be references, the values
from main() are passed by reference, and thus are
changed in main() as well.
References
provide the convenience and ease of use of normal
variables, with the power and pass-by-reference
capability of pointers!
|