Whenever you give two or more definitions to the same function name, the various function definitions must have different specifications for their arguments; that is, any two function de
Trang 1Now suppose your program also requires a function to compute the average of three numbers You might define a new function called ave3 as follows:
double ave3( double n1, double n2, double n3) {
return ((n1 + n2 + n3)/3.0);
}
This will work, and in many programming languages you have no choice but to do something like this However, C++ overloading allows for a more elegant solution In C++ you can simply use the same function name ave for both functions In C++ you can use the following function definition in place of the function definition ave3: double ave( double n1, double n2, double n3)
{ return ((n1 + n2 + n3)/3.0);
}
so that the function name ave then has two definitions This is an example of overload-ing In this case we have overloaded the function name ave Display 4.6 embeds these two function definitions for ave into a complete sample program Be sure to notice that each function definition has its own declaration (prototype)
The compiler can tell which function definition to use by checking the number and types of the arguments in a function call In the program in Display 4.6, one of the functions called ave has two arguments and the other has three arguments When there are two arguments in a function call, the first definition applies When there are three arguments in a function call, the second definition applies
Whenever you give two or more definitions to the same function name, the various function definitions must have different specifications for their arguments; that is, any two function definitions that have the same function name must use different numbers
of formal parameters or have one or more parameters of different types (or both) Notice that when you overload a function name, the declarations for the two different
definitions must differ in their formal parameters You cannot overload a function name
by giving two definitions that differ only in the type of the value returned Nor can you
O VERLOADING A F UNCTION N AME
If you have two or more function definitions for the same function name, that is called overload-ing. When you overload a function name, the function definitions must have different numbers of formal parameters or some formal parameters of different types When there is a function call, the compiler uses the function definition whose number of formal parameters and types of formal parameters match the arguments in the function call.
determining
which
definition
applies
Trang 2overload based on any difference other than the number or types of parameters You cannot overload based solely on const or solely on call-by-value versus call-by-reference parameters.1
1Some compilers will, in fact, allow you to overload on the basis of const versus no const, but you should not count on this The C++ standard says it is not allowed
Display 4.6 Overloading a Function Name
1 //Illustrates overloading the function name ave.
2 #include <iostream>
3 using namespace std;
4 double ave( double n1, double n2);
5 //Returns the average of the two numbers n1 and n2.
6
7 double ave( double n1, double n2, double n3);
8 //Returns the average of the three numbers n1, n2, and n3.
9 int main( )
10 {
11 cout << "The average of 2.0, 2.5, and 3.0 is "
12 << ave(2.0, 2.5, 3.0) << endl;
13 cout << "The average of 4.5 and 5.5 is "
14 << ave(4.5, 5.5) << endl;
15 return 0;
16 }
17 double ave( double n1, double n2)
18 {
19 return ((n1 + n2)/2.0);
20 }
21 double ave( double n1, double n2, double n3)
22 {
23 return ((n1 + n2 + n3)/3.0);
24 }
S AMPLE D IALOGUE
The average of 2.0, 2.5, and 3.0 is 2.5
The average of 4.5 and 5.5 is 5.0
Two arguments
Three arguments
Trang 3You already saw a kind of overloading in Chapter 1 (reviewed here) with the division operator, / If both operands are of type int, as in 13/2, then the value returned is the result of integer division, in this case, 6 On the other hand, if one or both operands are
of type double, then the value returned is the result of regular division; for example,
13/2.0 returns the value 6.5 There are two definitions for the division operator, /, and the two definitions are distinguished not by having different numbers of operands but rather by requiring operands of different types The only difference between overload-ing of / and overloading function names is that the C++ language designers have already done the overloading of /, whereas you must program the overloading of your function names yourself Chapter 8 discusses how to overload operators such as +, -, and so on
A UTOMATIC T YPE C ONVERSION AND O VERLOADING
Suppose that the following function definition occurs in your program and that you have not overloaded the function name mpg (so this is the only definition of a function called mpg).
double mpg( double miles, double gallons)
//Returns miles per gallon.
{ return (miles/gallons);
}
If you call the function mpg with arguments of type int, then C++ will automatically convert any argument of type int to a value of type double Hence, the following will output 22.5 miles per gallon to the screen:
cout << mpg(45, 2) << " miles per gallon";
C++ converts the 45 to 45.0 and the 2 to 2.0 and then performs the division 45.0/2.0 to obtain the value returned, which is 22.5.
If a function requires an argument of type double and you give it an argument of type int, C++ will automatically convert the int argument to a value of type double This is so useful and nat-ural that we hardly give it a thought However, overloading can interfere with this automatic type conversion Let’s look at an example.
S IGNATURE
A function’s ssssiiiig gnn g n naaa attttuu u urrrreeee is the function’s name with the sequence of types in the parameter list, not including the const keyword and not including the ampersand, & When you overload a func-tion name, the two definifunc-tions of the funcfunc-tion name must have different signatures using this def-inition of signature (Some authorities include the const and/or ampersand as part of the signature, but we wanted a definition that works for explaining overloading.)
interaction of
overloading
and type
conversion
Trang 4Self-Test Exercises
Suppose you had (foolishly) overloaded the function name mpg so that your program contained the following definition of mpg as well as the one previous:
int mpg( int goals, int misses)
//Returns the Measure of Perfect Goals //which is computed as (goals - misses).
{ return (goals - misses);
}
In a program that contains both of these definitions for the function name mpg, the following will (unfortunately) output 43 miles per gallon (since 43 is 45 - 2):
cout << mpg(45, 2) << " miles per gallon";
When C++ sees the function call mpg(45, 2), which has two arguments of type int, C++ first looks for a function definition of mpg that has two formal parameters of type int If it finds such
a function definition, C++ uses that function definition C++ does not convert an int argument to
a value of type double unless that is the only way it can find a matching function definition The mpg example illustrates one more point about overloading: You should not use the same function name for two unrelated functions Such careless use of function names is certain to even-tually produce confusion.
8 Suppose you have two function definitions with the following declarations:
double score( double time, double distance);
int score( double points);
Which function definition would be used in the following function call and why would it
be the one used? (x is of type double.) double finalScore = score(x);
9 Suppose you have two function definitions with the following declarations:
double theAnswer( double data1, double data2);
double theAnswer( double time, int count);
Which function definition would be used in the following function call and why would it
be the one used? (x and y are of type double.)
x = theAnswer(y, 6.0);
Trang 5■ RULES FOR RESOLVING OVERLOADING
If you use overloading to produce two definitions of the same function name with sim-ilar (but not identical) parameter lists, then the interaction of overloading and auto-matic type conversion can be confusing The rules that the compiler uses for resolving which of multiple overloaded definitions of a function name to apply to a given func-tion call are as follows:
1 Exact match: If the number and types of arguments exactly match a definition
(with-out any automatic type conversion), then that is the definition used
2 Match using automatic type conversion: If there is no exact match but there is a match
using automatic type conversion, then that match is used
If two matches are found at stage 1 or if no matches are found at stage 1 and two matches are found at stage 2, then there is an ambiguous situation and an error message will be issued
For example, the following overloading is dubious style, but is perfectly valid: void f( int n, double m);
void f( double n, int m);
However, if you also have the invocation
f(98, 99);
then the compiler does not know which of the two int arguments to convert to a value
of type double, and an error message is generated
To see how confusing and dangerous the situation can be, suppose you add the fol-lowing third overloading:
void f( int n, int m);
With this third overloading added, you no longer get an error message, since there is now an exact match Obviously, such confusing overloading is to be avoided
The above two rules will work in almost all situations In fact, if you need more pre-cise rules, you should rewrite your code to be more straightforward However, the exact rules are even a bit more complicated For reference value, we give the exact rules below Some of the terms may not make sense until you read more of this book, but do not be concerned The simple two rules given above will serve you well until you do understand the more complete rules
1 Exact match as described earlier
2 Matches using promotion within integer types or within floating-point types, such
as short to int or float to double (Note that bool-to-int and char-to-int conver-sions are considered promotions within integer types.)
3 Matches using other conversions of predefined types, such as int to double
4 Matches using conversions of user-defined types (see Chapter 8)
Trang 65 Matches using ellipses (This is not covered in this book, and if you do not use it,
it will not be an issue.)
If two matches are found at the first stage that a match is found, then there is an ambig-uous situation and an error message will be issued
R EVISED P IZZA -B UYING P ROGRAM
The Pizza Consumers Union has been very successful with the program that we wrote for it in Dis-play 4.5 In fact, now everybody always buys the pizza that is the best buy One disreputable pizza parlor used to make money by fooling consumers into buying the more expensive pizza, but our program has put an end to its evil practices However, the owners wish to continue their des-picable behavior and have come up with a new way to fool consumers They now offer both round pizzas and rectangular pizzas They know that the program we wrote cannot deal with rectangu-larly shaped pizzas, so they hope they can again confuse consumers Display 4.7 is another ver-sion of our program that compares a round pizza and a rectangular pizza Note that the function name unitPrice has been overloaded so that it applies to both round and rectangular pizzas
Display 4.7 Revised Pizza Program (part 1 of 3)
1 //Determines whether a round pizza or a rectangular pizza is the best buy.
2 #include <iostream>
3 using namespace std;
4 double unitPrice( int diameter, double price);
5 //Returns the price per square inch of a round pizza.
6 //The formal parameter named diameter is the diameter of the pizza
7 //in inches The formal parameter named price is the price of the pizza.
8 double unitPrice( int length, int width, double price);
9 //Returns the price per square inch of a rectangular pizza
10 //with dimensions length by width inches.
11 //The formal parameter price is the price of the pizza.
12 int main( )
13 {
14 int diameter, length, width;
15 double priceRound, unitPriceRound,
16 priceRectangular, unitPriceRectangular;
17 cout << "Welcome to the Pizza Consumers Union.\n";
18 cout << "Enter the diameter in inches"
19 << " of a round pizza: ";
20 cin >> diameter;
21 cout << "Enter the price of a round pizza: $";
Trang 7Display 4.7 Revised Pizza Program (part 2 of 3)
22 cin >> priceRound;
23 cout << "Enter length and width in inches\n"
24 << "of a rectangular pizza: ";
25 cin >> length >> width;
26 cout << "Enter the price of a rectangular pizza: $";
27 cin >> priceRectangular;
28 unitPriceRectangular =
29 unitPrice(length, width, priceRectangular);
30 unitPriceRound = unitPrice(diameter, priceRound);
31 cout.setf(ios::fixed);
32 cout.setf(ios::showpoint);
33 cout.precision(2);
34 cout << endl
35 << "Round pizza: Diameter = "
36 << diameter << " inches\n"
37 << "Price = $" << priceRound
38 << " Per square inch = $" << unitPriceRound
39 << endl
40 << "Rectangular pizza: Length = "
41 << length << " inches\n"
42 << "Rectangular pizza: Width = "
43 << width << " inches\n"
44 << "Price = $" << priceRectangular
45 << " Per square inch = $" << unitPriceRectangular
46 << endl;
47 if (unitPriceRound < unitPriceRectangular)
48 cout << "The round one is the better buy.\n";
49 else
50 cout << "The rectangular one is the better buy.\n";
51 cout << "Buon Appetito!\n";
52 return 0;
53 }
54 double unitPrice( int diameter, double price)
55 {
56 const double PI = 3.14159;
57 double radius, area;
58
59 radius = diameter/double(2);
60 area = PI * radius * radius;
61 return (price/area);
62 }
Trang 8■ DEFAULT ARGUMENTS
You can specify a default argument for one or more call-by-value parameters in a
func-tion If the corresponding argument is omitted, then it is replaced by the default argu-ment For example, the function volume in Display 4.8 computes the volume of a box from its length, width, and height If no height is given, the height is assumed to be 1
If neither a width nor a height is given, they are both assumed to be 1 Note that in Display 4.8 the default arguments are given in the function declaration but not in the function definition A default argument is given the first time the func-tion is declared (or defined, if that occurs first) Subsequent declarafunc-tions or a following definition should not give the default arguments again because some compilers will consider this an error even if the arguments given are consistent with the ones given previously
You may have more than one default argument, but all the default argument posi-tions must be in the rightmost posiposi-tions Thus, for the function volume in Display 4.8,
we could have given default arguments for the last one, last two, or all three parameters, but any other combinations of default arguments are not allowed
Display 4.7 Revised Pizza Program (part 3 of 3)
63 double unitPrice( int length, int width, double price)
64 {
65 double area = length * width;
66 return (price/area);
67 }
S AMPLE D IALOGUE
Welcome to the Pizza Consumers Union.
Enter the diameter in inches of a round pizza: 10
Enter the price of a round pizza: $8.50
Enter length and width in inches
of a rectangular pizza: 6 4
Enter the price of a rectangular pizza: $7.55
Round pizza: Diameter = 10 inches
Price = $8.50 Per square inch = $0.11
Rectangular pizza: Length = 6 inches
Rectangular pizza: Width = 4 inches
Price = $7.55 Per square inch = $0.31
The round one is the better buy.
Buon Appetito!
default argument
Trang 9If you have more than one default argument, then when the function is invoked, you must omit arguments starting from the right For example, note that in Display 4.8 there are two default arguments When only one argument is omitted, it is assumed
to be the last argument There is no way to omit the second argument in an invocation
of volume without also omitting the third argument
Display 4.8 Default Arguments
1
2 #include <iostream>
3 using namespace std;
4 void showVolume( int length, int width = 1, int height = 1);
5 //Returns the volume of a box
6 //If no height is given, the height is assumed to be 1.
7 //If neither height nor width is given, both are assumed to be 1.
8 int main( )
9 {
10 showVolume(4, 6, 2);
11 showVolume(4, 6);
12 showVolume(4);
13 return 0;
14 }
15 void showVolume( int length, int width, int height)
16 {
17 cout << "Volume of a box with \n"
18 << "Length = " << length << ", Width = " << width << endl
19 << "and Height = " << height
20 << " is " << length*width*height << endl;
21 }
S AMPLE D IALOGUE \
Volume of a box with
Length = 4, Width = 6
and Height = 2 is 48
Volume of a box with
Length = 4, Width = 6
and Height = 1 is 24
Volume of a box with
Length = 4, Width = 1
and Height = 1 is 4
Default arguments
A default argument should not
be given a second time.
Trang 10Self-Test Exercises
Default arguments are of limited value, but sometimes they can be used to reflect your way of thinking about arguments Default arguments can only be used with call-by-value parameters They do not make sense for call-by-reference parameters Any-thing you can do with default arguments can be done using overloading, although the default argument version will probably be shorter than the overloading version
10 This question has to do with the programming example entitled Revised Pizza-Buying
Program Suppose the evil pizza parlor that is always trying to fool customers introduces a
square pizza Can you overload the function unitPrice so that it can compute the price per square inch of a square pizza as well as the price per square inch of a round pizza? Why
or why not?
Testing and Debugging Functions
I beheld the wretch—the miserable monster whom I had created.
Mary Wollstonecraft Shelley, Frankenstein
This section reviews some general guidelines for testing programs and functions
■ THE assert MACRO
An assertion is a statement that is either true or false Assertions are used to document
and check the correctness of programs Preconditions and postconditions, which we discussed in Chapter 3, are examples of assertions When expressed precisely and in the syntax of C++, an assertion is simply a Boolean expression If you convert an assertion
to a Boolean expression, then the predefined macro assert can be used to check
whether or not your code satisfies the assertion (A macro is very similar to an inline
function and is used just like a function is used.) The assert macro is used like a void function that takes one call-by-value parame-ter of type bool Since an assertion is just a Boolean expression, this means that the argument to assert is an assertion When the assert macro is invoked, its assertion argument is evaluated If it evaluates to true, then nothing happens If the argument evaluates to false, then the program ends and an error message is issued Thus, calls to the assert macro are a compact way to include error checks within your program
For example, the following function declaration is taken from Programming Project 3:
void computeCoin( int coinValue, int & number, int & amountLeft);
//Precondition: 0 < coinValue < 100; 0 <= amountLeft < 100.
//Postcondition: number has been set equal to the maximum number
4.3
assertion
macro