C++ functions: Passing Arguments by Reference

Parameters can be passed by reference, which makes the formal parameter an alias of the actual argument. Thus, changes made to the parameters inside the function also made to the arguments.

When you invoke a function with a parameter, as described in the preceding sections, the pass-by-value  value of the argument is passed to the parameter. This is referred to as pass-by-value. If the argument is a variable rather than a literal value, the value of the variable is passed to the parameter. The variable is not affected, regardless of the changes made to the parameter inside the function. As shown in Listing 6.13, the value of x (1) is passed to the parameter n when invoking the increment function (line 14). n is incremented by 1 in the function (line 6), but x is not changed no matter what the function does.

Listing 6.13 Increment.cpp

1 #include <iostream>
2
using namespace std;
3
4
void increment(int n)
5 {
6    n++;
7    cout <<
“\tn inside the function is ” << n << endl;
8 }
9
10
int main()
11 {
12   
int x = 1;
13    cout <<
“Before the call, x is ” << x << endl;
14    increment(x);
15    cout <<
“after the call, x is ” << x << endl;
16
17   
return 0;
18 }

Pass-by-value has serious limitations. Listing 6.14 illustrates these. The program creates a function for swapping two variables. The swap function is invoked by passing two arguments. However, the values of the arguments are not changed after the function is invoked.

Listing 6.14 SwapByValue.cpp

1 #include <iostream>
2
using namespace std;
3
4
// Attempt to swap two variables does not work!
5 void swap(int n1, int n2)
6 {
7    cout <<
“\tInside the swap function” << endl;
8    cout <<
“\tBefore swapping n1 is ” << n1 <<
9   
” n2 is ” << n2 << endl;
10
11   
// Swap n1 with n2
12    int temp = n1;
13    n1 = n2;
14    n2 = temp;
15
16    cout <<
“\tAfter swapping n1 is ” << n1 <<
17   
“n2 is ” << n2 << endl;
18 }
19
20
int main()
21 {
22   
// Declare and initialize variables
23    int num1 = 1;
24   
int num2 = 2;
25
26    cout <<
“Before invoking the swap function, num1 is ”
27    << num1 << ” and num2 is ” << num2 << endl;
28
29   
// Invoke the swap function to attempt to swap two variables
30    swap(num1, num2);
31
32    cout <<
“After invoking the swap function, num1 is ” << num1 <<
33   
” and num2 is ” << num2 << endl;
34
35   
return 0;
36 }

Before the swap function is invoked (line 30), num1 is 1 and num2 is 2. After the swap function is invoked, num1 is still 1 and num2 is still 2. Their values have not been swapped.

As shown in Figure 6.6, the values of the arguments numl and num2 are passed to n1 and n2, but nl and n2 have their own memory locations independent of numl and num2. Therefore, changes in nl and n2 do not affect the contents of numl and num2.

Another twist is to change the parameter name nl in swap to numl. What effect does this have? No change occurs, because it makes no difference whether the parameter and the argu­ment have the same name. The parameter is a variable in the function with its own memory space. The variable is allocated when the function is invoked, and it disappears when the function is returned to its caller.

The swap function attempts to swap two variables. After the function is invoked, though, the values of the variables are not swapped, because the values of variables are passed to the parameters. The original variables and parameters are independent. Even though the values in the called function are changed, the values in the original variables are not.

So, can we write a function to swap two variables? Yes. The function can accomplish this reference variable by passing a reference to the variables. C++ provides a special type of variable—a reference variable—which can be used as a function parameter to reference the original variable. You can access and modify the original data stored in that variable through its reference variable. A reference variable is an alias for another variable. To declare a reference variable, place the ampersand (&) in front of the variable or after the data type for the variable. For example, the following code declares a reference variable r that refers to variable count.

int &r = count;

or equivalently,

int& r = count;

Listing 6.15 gives an example of using reference variables.

Listing 6.15 TestReferenceVariable.cpp

1 #include <iostream>
2
using namespace std;
3
4
int main()
5 {
6
int count = 1;
7
int& r = count;
8 cout <<
“count is ” << count << endl;
9 cout <<
“r is ” << r << endl;
10
11 r++;
12 cout <<
“count is ” << count << endl;
13 cout <<
“r is ” << r << endl;
14
15 count =
10;
16 cout <<
“count is ” << count << endl;
17 cout <<
“r is ” << r << endl;
18
19
return 0;
20 }

Line 7 declares a reference variable named r that is merely an alias for count. As shown in Figure 6.7a, r and count reference the same value. Line 11 increments r, which in effect increments count, since they share the same value, as shown in Figure 6.7b.

Line 15 assigns 10 to count. Since count and r refer to the same value. Both count and r are now 10.

You can use a reference variable as a parameter in a function and pass a regular variable to invoke the function. The parameter becomes an alias for the original variable. This is known as pass-by-reference. You can access and modify the value stored in the original variable through the reference variable. To demonstrate the effect of pass-by-reference, let us rewrite the increment function in Listing 6.13 as shown in Listing 6.16.

Listing 6.16 IncrementWithPassByReference.cpp

1 #include <iostream>
2
using namespace std;
3
4
void increment(int& n)
5 {
6    n++;
7    cout <<
“n inside the function is ” << n << endl;
8 }
9
10
int main()
11 {
12   
int x = 1;
13    cout <<
“Before the call, x is ” << x << endl;
14    increment(x);
15    cout <<
“After the call, x is ” << x << endl;
16
17   
return 0;
18 }

Invoking increment(x) in line 14 passes the reference of variable x to the reference variable n in the increment function. Now n and x are the same, as shown in the output. Incrementing n in the function (line 6) is the same as incrementing x. So, before the function is invoked, x is 1, and afterward, x becomes 2.

Pass-by-value and pass-by-reference are two ways of passing arguments to the parameters of a function. Pass-by-value passes the value to an independent variable and pass-by-reference shares the same variable. Semantically pass-by-reference can be described as pass-by-sharing.

Now you can use reference parameters to implement a correct swap function, as shown in Listing 6.17.

Listing 6.17 SwapByReference.cpp

1 #include <iostream>
2
using namespace std;
3
4
// Swap two variables
5 void swap(int& n1, int& n2)
6 {
7    cout <<
“\tInside the swap function” << endl;
8    cout <<
“\tBefore swapping n1 is ” << n1 <<
9   
” n2 is ” << n2 << endl;
10
11   
// Swap n1 with n2
12    int temp = n1;
13    n1 = n2;
14    n2 = temp;
15
16    cout <<
“\tAfter swapping n1 is ” << n1 <<
17   
” n2 is ” << n2 << endl;
18 }

19
20
int main()
21 {
22   
// Declare and initialize variables
23    int num1 = 1;
24   
int num2 = 2;
25
26    cout <<
“Before invoking the swap function, num1 is ”
27    << num1 << ” and num2 is ” << num2 << endl;
28
29   
// Invoke the swap function to attempt to swap two variables
30    swap(num1, num2);
31
32    cout <<
“After invoking the swap function, num1 is ” << num1 <<
33   
” and num2 is ” << num2 << endl;
34
35   
return 0;
36 }

Before the swap function is invoked (line 30), num1 is 1 and num2 is 2. After the swap function is invoked, num1 becomes 2 and num2 becomes 1. Their values have been swapped. As shown in Figure 6.8, the references of num1 and num2 are passed to n1 and n2, so n1 and num1 are alias and n2 and num2 are alias. Swapping values between n1 and n2 is the same as swapping values between num1 and num2.

When you pass an argument by reference, the formal parameter and the argument must have the same type. For example, in the following code, the reference of variable x is passed to the function, which is fine. However, the reference of variable y is passed to the function, which is wrong, since y and n are of different types.

#include <iostream> using namespace std;

void increment(double& n)

{

n++;

}

int main()

{

double x = 1;

int y = 1;

increment(x);

increment(y); // Cannot invoke increment(y) with an int argument

cout << “x is ” << x << endl;

cout << “y is ” << y << endl;

return 0;

}

When you pass an argument by reference, the argument must be a variable. When you pass an argument by value, the argument can be a literal, a variable, an expression, or even the return value of another function.

Source: Liang Y. Daniel (2013), Introduction to programming with C++, Pearson; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *