UMBC CMSC202, Computer Science II, Fall 1998,
Sections 0101, 0102, 0103, 0104
3. Reference Parameters & Function Overloading
Tuesday September 08, 1998
[Previous Lecture]
[Next Lecture]
Assigned Reading: 2.5-2.9
Handouts (available on-line):
Project 1
Programs from this lecture.
Topics Covered:
- Recap from previous lecture: In the
header file box2a.h, the function
orientation() is declared as an inline function. This directs the
compiler to attempt to use textual substitution instead of a function call,
when it compiles code for a call to orientation(). Thus, using
inline functions we can avoid the overhead of function calls, but still
maintain limited access to the private member top. Also note that
since the orientation() function is such a simple function, we can
easily provide the implementation of the function inside the class
declaration. This feature of C++ should be reserved for very small
implementations. Otherwise the class declarations would be unreadable.
- Reference parameters:
- There are two ways to pass a parameter to a function: by value and by
reference. (These are not the only two parameter passing schemes --- e.g.
there is parameter passing by name in ALGOL --- but these are the two we
will discuss.) In C, parameter passing by reference is simulated by
passing a pointer to the variable. The pointer itself is passed by value,
so technically C does not have a mechanism for parameter passing by
reference. C++ adds this feature.
- In C, there are 2 reasons for wanting to pass a parameter by
reference:
- We want the function to be able to modify the value of the parameter.
(This should be avoided if possible.)
- We want to pass a parameter without copying. For example, when we
pass a large array to a function, we usually do not want to make a new copy
of the array because this can take a long time and uses lots of memory.
- In C++, a reference parameter x of type T, would
appear in the function header as T& x. The use of the &
symbol is not consistent with the rest of the C/C++ language, so try to
ignore the fact that & is also used as the address of operator. A
consistent interpretation of the declaration T& x would be
that the address of x has type T. This is not
the correct interpretation. The correct (but inconsistent) interpretation
of T& x is simply that x is a reference parameter of
type T. Don't try to make sense out of this! It doesn't!
- We look at references more closely in a series of examples. In the
first program, we
note the use of reference variables. After the following declaration:
int x = 3, y = 9 ;
int &ref = x ; // initialize reference
the variable ref becomes an alias for the variable
x. In the rest of the program, using the variable ref is
equivalent to the variable x. That is, assigning values to
ref has the same effect as assigning values to x. Also,
ref can be used as a normal int variable, because it is a
reference and not a pointer. The we can only associate a reference
variable with another variable by initializing its value, as shown above
for ref. After the initialization, we cannot make ref an
alias of a different variable, say y --- again, because a
reference is not a pointer. Assigning, y to ref
simply assigns the value stored in y to x, as the sample run demonstrates.
It is best to think of the references as going through two stages. In the
first stage, the reference is initialized and becomes the alias of
another variable. In the second stage, the reference is used just like
the variable it is aliasing.
- Our second program demonstrates a
simple use of a reference parameter. In the function add3():
void add3(int &a) {
a = a + 3 ;
}
the parameter a is a reference parameter. Thus, changing the
value of a in the function, changes the value of the actual
parameter in the main program. Note that the syntax for using a
is the same as an int variable, because (beating a dead horse)
references are NOT pointers. Also, note that from the main program, the
add3 function is called using the syntax:
add3(x) ;
In C, you would expect that such a function call cannot change the value of
x. In C++, because of reference parameters, you have to look at
the function prototype to determine whether a function can modify the
values of the actual parameters. (See sample
run.)
- Our third program shows that you can
write the familiar "swap" function using reference parameters. See sample run.
- Recall that we want reference parameters for the two reasons listed
above: changing the value of the actual parameter and avoiding copying.
Our next program shows that we can avoid
copying without letting the function change the value of the actual
parameter. This is accomplished by designating the parameters as
const reference parameters. In this example, the function
add() tries to modify the value of the reference parameter
a. The sample run shows the
error message given by the compiler for trying to modify the value of a
const parameter.
- In the last program, we show another
benefit of using constant reference parameters. This time the
add() function has the prototype:
int add(int &a, const int &b) ;
Since b is a constant reference parameter, we can pass a constant
6 by reference in the call:
z = add(x, 6) ;
This is convenient in many situations. See sample run.
- Function Overloading:
- C++ allows you to have several functions using the same name within
the same scope, as long as the functions can be distinguished by the number
and type of their parameters (also known as the signature of the
functions). We explore overloading functions with several programs.
- In the first program, we have 4
functions called foo(). However, each function has a distinct
signature, so the compiler has no trouble figuring out which one should be
used by which function call. (See sample
run.)
- The second program shows that
the type of the return value of a function is not considered part of its
signature. Here we have two functions called foo(). Both
functions take two integer parameters, so they have the same signature. One
function returns an int and the has return type void.
However, in C/C++ the return value does not have to be used, so it is
impossible for the compiler to determine which function should be used. The
sample run shows the error message
given by the compiler in such a situation.
- The third program shows that
the compiler is smart enough to determine which function should be used
even when a type conversion is required. In this program, the two
functions called foo() take either two integers or a float and an
integer. When foo() is called with an integer and a float, the
compiler calls the first function because that only involves one type
conversion (calling the second function would require two type
conversions). Similarly, when foo() is called with two floats,
there is a unique best choice of which foo() to use, so the
compiler is not confused. (See sample
run.)
- On the other hand, in the fourth
program, we have two functions named foo() which either takes
two integers or two floats. When foo() is called with an integer
and a float, the compiler cannot determine which function to use because
calling either function involves one type conversion. The sample run shows the error message that
the compiler gives in this situation.
- Our fifth program shows
that for reference parameters, the following two functions have
different signatures:
void foo(int a, int& b) ;
void foo(int a, const int& b) ;
I.e., adding the const designation to int& b results in a
different signature. (This example was shown incorrectly in class.)
For example, the function call foo(x,y) results in a call to
the first function, but the function call foo(x,6) calls the
second function. (See sample
run.)
- Operator Overloading:
- C++ also let's you overload operators such as +, -, * and /.
Relational operators like < and <= can also be overloaded. Even the
<< and >> operators used with cout and cin can be
overloaded. One use of this is to define arithmetic operations for new
numeric classes. We quickly looked at a fraction class which does this.
(See header file and
implementation.) We will
return to operator overloading in another lecture.
- Two programs and sample runs show that Fraction objects can now
be added using + and compared using < just as we do with int's and
float's.
- First program and
sample run.
- Second program and
sample run
- Why is operator overloading a good thing? If we take a quick peek
ahead at templates, we can see one reason. In the template header file bubble.h we describe a function
BubbleSort() that can sort arrays of type DATA. Here DATA is a
placeholder for any type, or class, that supports comparison and assignment.
Thus, having written only one BubbleSort() function, we can use it
for many types. Our main
program that uses bubble.h calls BubbleSort 3 times
to sort int's, float's and Fractions. (See sample run.)
- Discussed Project 1.
[Previous Lecture]
[Next Lecture]
Last Modified:
22 Jul 2024 11:28:49 EDT
by
Richard Chang
Back up
to Fall 1998 CMSC 202 Section Homepage