4.6 Overloading Function Names

“…—and that shows that there are three hundred and sixty-four days when you might get un-birthday presents—”

“Certainly,” said Alice.

“And only one for birthday presents, you know. There’s glory for you!”

“I don’t know what you mean by ‘glory,’” Alice said.

Humpty Dumpty smiled contemptuously, “Of course you don’t—till I tell you. I mean ‘there’s a nice knock-down argument for you!’”

“But ‘glory’ doesn’t mean ‘a nice knock-down argument,’” Alice objected.

“When I use a word,” Humpty Dumpty said, in rather a scornful tone,

“it means just what I choose it to mean—neither more nor less.”

“The question is,” said Alice, “whether you can make words mean so many different things.”

“The question is,” said humpty dumpty, “which is to be master—that’s all.”

LEWIS CARROLL, Through the Looking-Glass

C++ allows you to give two or more different definitions to the same function name, which means you can reuse names that have strong intuitive appeal across a variety of situations. For example, you could have three functions called max: one that computes the largest of two numbers, another that computes the largest of three numbers, and yet another that computes the largest of four numbers. When you give two (or more) function definitions for the same function name, that is called overloading the function name. Overloading does require some extra care in defining your functions and should not be used unless it will add greatly to your program’s readability. But when it is appropriate, overloading can be very effective.

Introduction to Overloading

Suppose you are writing a program that requires you to compute the average of two numbers. You might use the following function definition:

double ave(double n1, double n2)
{
     return ((n1 + n2)/2.0);
}

Now 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. Fortunately, C++ allows for a more elegant solution. In C++ you can simply use the same function name ave for both functions; 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);
}

The function name ave now has two definitions. This is an example of overloading. In this case we have overloaded the function name ave. In Display 4.17 we have embedded these two function definitions for ave into a complete sample program. Be sure to notice that each function definition has its own function declaration.

Display 4.17 Overloading a Function Name

An illustration shows a code segment describing the overloading of a function Name.

Output

The average of 2.0, 2.5, and 3.0 is 2.50000
The average of 4.5 and 5.5 is 5.00000

Overloading is a great idea. It makes a program easier to read, and it saves you from going crazy trying to think up a new name for a function just because you already used the most natural name in some other function definition. But how does the compiler know which function definition to use when it encounters a call to a function name that has two or more definitions? The compiler cannot read a programmer’s mind. In order to tell which function definition to use, the compiler checks the number of arguments and the types of the arguments in the function call. In the program in Display 4.17, one of the functions called ave has two arguments and the other has three arguments. To tell which definition to use, the compiler simply counts the number of arguments in the function call. If there are two arguments, it uses the first definition. If there are three arguments, it uses the second definition.

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 use formal parameters of different types (or both). Notice that when you overload a function name, the function 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.

Overloading is not really new to you. You saw a kind of overloading in Chapter 2 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 returned 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 difference between overloading of / and overloading function names is that the compiler has already done the overloading of / but you program the overloading of the function name. We will see in a later chapter how to overload operators such as +, , and so on.

Programming Example Revised Pizza-Buying Program

The Pizza Consumers Union has been very successful with the program that we wrote for it in Display 4.10. 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 their evil practices. However, the owners wish to continue their despicable 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 rectangularly shaped pizzas, so they hope they can again confuse consumers. We need to update our program so that we can foil their nefarious scheme. We want to change the program so that it can compare a round pizza and a rectangular pizza.

The changes we need to make to our pizza evaluation program are clear: We need to change the input and output a bit so that it deals with two different shapes of pizzas. We also need to add a new function that can compute the cost per square inch of a rectangular pizza. We could use the following function definition in our program so that we can compute the unit price for a rectangular pizza:

double unitPriceRectangular
    (int length, int width, double price)
{     double area = length * width;
     return (price/area);
}

However, this is a rather long name for a function; in fact, it’s so long that we needed to put the function heading on two lines. That is legal, but it would be nicer to use the same name, unitPrice, for both the function that computes the unit price for a round pizza and for the function that computes the unit price for a rectangular pizza. Since C++ allows overloading of function names, we can do this. Having two definitions for the function unitPrice will pose no problems to the compiler because the two functions will have different numbers of arguments. Display 4.18 shows the program we obtained when we modified our pizza evaluation program to allow us to compare round pizzas with rectangular pizzas.

Display 4.18 Overloading a Function Name

 1     //Determines whether a round pizza or a rectangular pizza is the best buy.
 2     #include <iostream>
 3
 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
 9     double unitPrice(int length, int width, double price);
10     //Returns the price per square inch of a rectangular pizza
11     //with dimensions length by width inches.
12     //The formal parameter price is the price of the pizza.
13
14     int main( )
15     {
16         using namespace std;
17         int diameter, length, width;
18         double priceRound, unitPriceRound,
19         priceRectangular, unitPriceRectangular;
20
21         cout << "Welcome to the Pizza Consumers Union.\n";
22         cout << "Enter the diameter in inches"
23              << " of a round pizza: ";
24         cin >> diameter;
25         cout << "Enter the price of a round pizza: $";
26         cin >> priceRound;
27         cout << "Enter length and width in inches\n"
28              << "of a rectangular pizza: ";
29         cin >> length >> width;
30         cout << "Enter the price of a rectangular pizza: $";
31         cin >> priceRectangular;
32
33         unitPriceRectangular =
34                   unitPrice(length, width, priceRectangular);
35         unitPriceRound = unitPrice(diameter, priceRound);
36
37         cout.setf(ios::fixed);
38         cout.setf(ios::showpoint);
39         cout.precision(2);
40         cout << endl
41              << "Round pizza: Diameter = "
42              << diameter << " inches\n"
43              << "Price = $" << price_round
44              << " Per square inch = $" << unitPriceRound
45              << endl
46              << "Rectangular pizza: Length = "
47              << length << " inches\n"
48            << "Rectangular pizza: Width = "
49            << width << " inches\n"
50            << "Price = $" << priceRectangular
51            << " Per square inch = $" << unitPriceRectangular
52            << endl;
53
54        if (unitPriceRound < unitPriceRectangular)
55           cout << "The round one is the better buy.\n";
56        else
57           cout << "The rectangular one is the better buy.\n";
58
59        cout << "Buon Appetito!\n";
60        return 0;
61     }
62
63    double unitPrice(int diameter, double price)
64    {
65         const double PI = 3.14159;
66         double radius, area;
67
68         radius = diameter/static_cast<double>(2);
69         area = PI * radius * radius;
70         return (price/area);
71    }
72
73    double unitPrice(int length, int width, double price)
74    {
75         double area = length * width;
76         return (price/area);
77    }

Sample Dialogue

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!

Automatic Type Conversion

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, 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 natural that we hardly give it a thought. However, overloading can interfere with this automatic type conversion. Let’s look at an example.

Now, suppose you had (foolishly) overloaded the function name mpg so that your program also contained the following definition of mpg (as well as the previous one):

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 eventually produce confusion.

Self-Test Exercises

  1. Suppose you have two function definitions with the following function 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.)

    finalScore = score(x);
  2. Suppose you have two function definitions with the following function 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);
  3. Suppose you have two function definitions with the function declarations given in Self-Test Exercise 25. Which function definition would be used in the following function call and why would it be the one used?

    x = theAnswer(5, 6);
  4. Suppose you have two function definitions with the function declarations given in Self-Test Exercise 25. Which function definition would be used in the following function call and why would it be the one used?

    x = theAnswer(5, 6.0);
  5. This question has to do with the Programming Example “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?

  6. Look at the program in Display 4.18. The main function contains the using directive:

    using namespace std;

    Why doesn’t the method unitprice contain this using directive?