ADVANCED PLACEMENT Computer Science - C++
Lesson 7 - Functions and Reference Parameters

INTRODUCTION:                
In the previous lesson, you learned to write functions that received values and returned one value.  In this lesson you will learn about a very important topic called reference parameters.  These types of parameters enable you to return more than one value from a function.  In addition, you will learn about other function tools, which enable us to enhance the performance of functions.  This lesson completes our study of the syntax of functions and tools for writing functions well.  In the next series of lessons, you will fill your functions with control structures to solve more demanding problems.

The key topics for this lesson are:

A.    Reference Parameters and Functions
B.    Default Arguments
C.    Overloaded Functions
D.    Function Templates 

VOCABULARY:              

ALIAS -- A function uses a local variable to refer to the memory location of the variable passed to the function.

REFERENCE PARAMETERS -- Used to modify the parameters sent to a function.

ADDRESS OPERATOR -- Returns the memory address of a variable.

CONST REFERENCE PARAMETERS -- Used to pass a large data item to a function and protect it from any changes. 

DEFAULT ARGUMENTS -- C++ allows the user to specify these items when declaring a function. Some functions may use a fairly common value as one of its parameters.

OVERLOADED FUNCTIONS -- A programming strategy used to code a specific algorithm to work with a variety of data types

FUNCTION TEMPLATES -- This code can be written to replace the many implementations needed to overload a function.

 DISCUSSION:                       

A.  Reference Parameters and Functions

1.   It is often necessary to modify the parameters sent to a function.  For example, a swap routine which swaps the contents of two variables is a very useful function.  The function will work with the memory locations of two variables and it will swap their contents.

2.    Before we look at examples of reference parameters we need to understand the concept of an "alias" and memory addresses.  Observe the behavior of the following two programs.

Program 7-1
#include <iostream.h>

main ()
{
         int  a = 2, b;
         b = a;       // b now also stores 2
         cout << "The address of a = " << &a << endl;
         cout << "The address of b = " << &b << endl;
         return 0;
}

Run Output (machine dependent):
The address of a = 0x8f7ffff4
The address of b = 0x8f7ffff2

    Both variables a and b contain the value 2, but each is located in a different memory location.  Notice the use of the address operator (&) which returns the memory address of a variable. 

Program 7-2

#include <iostream.h> 

main ()
{
         int  a = 2;
         int  &b = a;     //  b is a reference variable
         cout << "a = " << a << "  b = " << b << endl;
         b = 7;
         cout << "a = " << a << "  b = " << b << endl;
         cout << "The address of a = " << &a << endl;
         cout << "The address of b = " << &b << endl;
         return 0;
}

Run Output

a = 2   b = 2
a = 7   b = 7
The address of a = 0x8f83fff4
The address of b = 0x8f83fff4
 

       In Program 7-2, the variable b is a reference variable for integer variable a.  We have created b as an alias for a.  Any reference to b is also a reference to a.  Notice that variables a and b are identifiers for the same memory location.

        Reference variables must be initialized when they are declared and they cannot be reassigned as aliases to other variables.  References can be declared and used within a function (as in Program 7-1) but there is little reason to do so.  It makes more sense to just use the local variable instead of an alias.

3.    We can now construct a swap routine using reference parameters.

Program 7-3

#include <iostream.h>
void swap (int &, int &);              // function prototype

main ()
{
         int  a = 3, b = 5;
         cout << "Before swap:  a = " << a << "  b = " << b << endl
         swap (a,b);             // actual parameters
         cout << "After swap:  a = " << a << "  b = " << b << endl;
         return 0;
}

 void swap (int &one, int &two)  // formal parameters
{
         int  temp = one;
         one = two;
         two = temp;
}

Run Output:

Before swap:  a = 3  b = 5
After swap:  a = 5  b = 3

       Here is the sequence of events:

a.     When function swap is called, the actual parameters passed to the swap function are the variables a and b in function main.

b.    In function swap, the reference parameters &one and &two become alias names for variables a and b in function main.  Any changes made to one or two inside of function swap are direct changes to the memory locations named a and b in function main.

 

4.    Reference parameters are often referred to as two-way parameters.  Data flows into a function.  Changes to this data are sent back out of the function by means of the alias variable. 

5.    The next example consists of a function which obtains two floats from the keyboard and returns the values to the calling function.

Program 7-4

#include <iostream.h>
void  get2Floats (float &, float &);

main ()
{
         float   x,y;
         get2Floats (x,y);
         cout << "x = " << x << "   y = " << y << endl;
         return 0;
}

void  get2Floats (float &data1, float &data2)
{
         cout << "Enter float 1 ---> ";
         cin >> data1;
         cout << "Enter float 2 ---> ";
         cin >> data2;
}

Run output:

Enter float1 --->  2.75
Enter float2 --->  9.17

x = 2.75   y = 9.17

6.    It is important to note that when a reference parameter is used to receive values into a function, a copy is not made of the information.  The function uses a local alias to refer to the memory location of the variable passed to the function.  Reference parameters do not involve the overhead of copying data from one memory location to another as is the case with value parameters.

7.    There will be occasions where we need to pass a large data item to a function but we want to protect it from any changes.  A value parameter does protect the actual argument being passed, but at a cost of time and memory.  A solution is to use a const reference parameter. 

void printItem (const int &value);

       The reference argument &value is an alias for another variable, but the const qualifier prevents any changes to value.  This combination gives us the performance of reference parameters but the protection of value parameters.

B.    Default Arguments

1.    C++ allows the user to specify default arguments when declaring a function.  Some functions may use a fairly common value as one of its parameters as shown in this example from physics.

float force (float mass, float acceleration = 9.81)

//  Newton's 2nd Law:  F = ma, a = 9.81 m/s2 on earth

{
         return (mass * acceleration);
}

       When this function is called, we can feed it both values or just the mass.  If the acceleration value is omitted in the function call, the function will use the default 9.81. 

       force (1), returns 9.81
       force (5), returns 49.05
       force (70,3.70), returns 259.0

       In the last case, the value of 3.70 will override the default 9.81.  (The acceleration of gravity on Mars is a = 3.70 m/s2.)

2.    The default values must be the right-hand values declared in the parameter list.  The values omitted when the function is called must be to the right of any explicit values.

3.    Some programmers feel that using default values leads to obscure code.  It is probably easier to read code when all the values being fed to a function are listed.

4.     However, when we get to the implementation of classes, (Lesson 27), we will initialize objects with default values.  The idea of default values is a good one in some situations. 

C.    Overloaded Functions

1.    Overloading is a programming strategy  used to code a specific algorithm to work with a variety of data types.  For example, the + operator in C++ is overloaded to add integers, floats, even characters.  We would say that the + operator is flexible because it can work with a variety of situations.

2.    Some functions will end up solving the same fundamental tasks using different data types.  For example, the swap routine would be coded the same to swap integers, floats, and characters.  Instead of coding a swapint, swapfloat, and swapchar function, we can overload a swap function in C++.

3.    Here is an implementation of an overloaded swap function:

Program 7-5

#include <iostream.h>

void swap (int &, int &);
void swap (float &, float &);
void swap (char &, char &); 
main ()
{
         int  s = 5, t = 8;      float  x = 1.25, y = 6.38;char  c1 = 'j', c2 = 'k';
         swap (s,t);       cout << "s = " << s << "  t = " << t << endl;
         swap (x,y);      cout << "x = " << x << "  y = " << y << endl;
         swap (c1,c2);  cout << "c1 = " << c1 << "  c2 = " << c2 << endl;
         return 0;
}
void swap (int &a, int &b)  
{
     int
temp=a
     a
=b
     b
=temp;
}

void swap ( float &a float &b) //parameters modifying swap, reference the a matches the x and the b matches the y   
  
{
        float
temp=a
        a
=b;
        b=temp;
    }

void swap (char &a, char &b)
{
    char
temp=a
     a
=b
     b
=temp;
} 

Run output:

s = 8   t = 5
x = 6.38   y = 1.25
c1 = k   c2 = j
 

4.    When the swap function is called the compiler selects the proper version depending on the data types in the parameter list.

5.    Overloading is a valuable technique that will be learned and applied to operators and classes.  You have already been using overloaded stream operators with objects cin and cout.  The output operator (<<) works with text constants, integers, floats, etc.  This operator has been overloaded to work with many different data types.

D.    Function Templates 

1.    If the code for an overloaded function is identical regardless of the data type used, a single function template can be written to replace the many implementations needed to overload a function.  The compiler will take a single template for a function and create the appropriate object code depending on the data type sent to the function.

2.    The swap routine will continue to be our example.

Program 7-6
#include <iostream.h>

template <class dataType>
void  swap (dataType &,  dataType &);

main ()
{
         int  s = 5, t = 8;float  x = 1.25, y = 6.38; char  c1 = 'j', c2 = 'k';
         swap (s,t);          cout << "s = " << s << "  t = " << t << endl;
         swap (x,y);         cout << "x = " << x << "  y = " << y << endl;
         swap (c1,c2);     cout << "c1 = " << c1 << "  c2 = " << c2 << endl;
         return 0;
}

template <class dataType>
void  swap (dataType &a,  dataType &b)
{
         dataType temp = a;  a = b;  b = temp;

3.    The run output for Program 7-6 is the same as Program 7-5.  The identifier dataType in the function prototype will assume whatever data type is sent to the function.  If the swap routine is called with two integers, then dataType represents int.  If two floats are sent, dataType represents float.  Notice that the local variable temp inside of swap is also declared of type dataType .

4.    The function template for swap returned no data type.  If the return type of the function is dependent on the data type of the incoming parameters we can also make the return type flexible.  Here is an example using a squaring function:

template <class someType>

someType  square  (someType  number)

{
         return  number * number;
}

Lesson Pointers

SUMMARY/REVIEW:          The last two lessons have covered the material needed to write functions.  We are now ready to deal with larger and more difficult problems using functions in a divide-and-conquer approach.  The next lessons move on to the control structures of C++.

ASSIGNMENT:                     Worksheet, W.A.7.1, Parameters       

                                                  Lab Exercise, L.A.7.1, Fun2