A custom-tailored suit always fits better than one off the rack.
MY UNCLE, The Tailor
In the previous section we told you how to use predefined functions. In this section we tell you how to define your own functions.
You can define your own functions, either in the same file as the main
part of your program or in a separate file so that the functions can be used by several different programs. The definition is the same in either case, but for now, we will assume that the function definition will be in the same file as the main
part of your program.
Display 4.3 contains a sample function definition in a complete program that demonstrates a call to the function. The function is called totalCost
. The function takes two arguments—the price for one item and number of items for a purchase. The function returns the total cost, including sales tax, for that many items at the specified price. The function is called in the same way a predefined function is called. The description of the function, which the programmer must write, is a bit more complicated.
The description of the function is given in two parts that are called the function declaration
and the function definition
. The function declaration (also known as the function prototype) describes how the function is called. C++ requires that either the complete function definition or the function declaration appears in the code before the function is called. The function declaration for the function totalCost
is in color at the top of Display 4.3 and is reproduced here:
double totalCost(int numberPar, double pricePar);
The function declaration tells you everything you need to know in order to write a call to the function. It tells you the name of the function, in this case totalCost
. It tells you how many arguments the function needs and what type the arguments should be; in this case, the function totalCost
takes two arguments, the first one of type int
and the second one of type double
. The identifiers numberPar
and pricePar
are called formal parameters. A formal parameter is used as a kind of blank, or place holder, to stand in for the argument. When you write a function declaration, you do not know what the arguments will be, so you use the formal parameters in place of the arguments. The names of the formal parameters can be any valid identifiers, but for a while we will end our formal parameter names with Par
so that it will be easier for us to distinguish them from other items in a program. Notice that a function declaration ends with a semicolon.
The first word in a function declaration specifies the type of the value returned by the function. Thus, for the function totalCost
, the type of the value returned is double
.
As you can see, the function call in Display 4.3 satisfies all the requirements given by its function declaration. Let’s take a look. The function call is in the following line:
bill = totalCost(number, price);
The function call is the expression on the right-hand side of the equal sign. The function name is totalCost
, and there are two arguments: The first argument is of type int
, the second argument is of type double
, and since the variable bill
is of type double
, it looks like the function returns a value of type double
(which it does). All that detail is determined by the function declaration.
The compiler does not care whether there’s a comment along with the function declaration, but you should always include a comment that explains what value is returned by the function.
In Display 4.3 the function definition is in color at the bottom of the display. A function definition describes how the function computes the value it returns. If you think of a function as a small program within your program, then the function definition is like the code for this small program. In fact, the syntax for the definition of a function is very much like the syntax for the main
part of a program. A function definition consists of a function header followed by a function body. The function header is written the same way as the function declaration, except that the header does not
have a semicolon at the end. This makes the header a bit repetitious, but that’s OK.
Although the function declaration tells you all you need to know to write a function call, it does not tell you what value will be returned. The value returned is determined by the statements in the function body. The function body follows the function header and completes the function definition. The function body consists of declarations and executable statements enclosed within a pair of braces. Thus, the function body is just like the body of the main
part of a program. When the function is called, the argument values are plugged in for the formal parameters and then the statements in the body are executed. The value returned by the function is determined when the function executes a return
statement. (The details of this “plugging in” will be discussed in a later section.)
A return
statement consists of the keyword return
followed by an expression. The function definition in Display 4.3 contains the following return
statement:
return (subtotal + subtotal * TAX_RATE);
When this return
statement is executed, the value of the following expression is returned as the value of the function call:
(subtotal + subtotal * TAX_RATE)
The parentheses are not needed. The program will run exactly the same if the return
statement is written as follows:
return subtotal + subtotal * TAX_RATE;
However, on larger expressions, the parentheses make the return
statement easier to read. For consistency, some programmers advocate using these parentheses even on simple expressions. In the function definition in Display 4.3, there are no statements after the return
statement, but if there were, they would not be executed. When a return
statement is executed, the function call ends.
Let’s see exactly what happens when the following function call is executed in the program shown in Display 4.3:
bill = totalCost(number, price);
First, the values of the arguments number
and price
are plugged in for the formal parameters; that is, the values of the arguments number
and price
are substituted in for numberPar
and pricePar
. In the Sample Dialogue, number
receives the value 2
and price
receives the value 10.10
. So 2
and 10.10
are substituted for numberPar
and pricePar
, respectively. This substitution process is known as the call-by-value mechanism, and the formal parameters are often referred to as call-by-value formal parameters, or simply as call-by-value parameters. There are three things that you should note about this substitution process:
It is the values of the arguments that are plugged in for the formal parameters. If the arguments are variables, the values of the variables, not the variables themselves, are plugged in.
The first argument is plugged in for the first formal parameter in the parameter list, the second argument is plugged in for the second formal parameter in the list, and so forth.
When an argument is plugged in for a formal parameter (for instance, when 2
is plugged in for numberPar
), the argument is plugged in for all instances of the formal parameter that occur in the function body (for instance, 2
is plugged in for numberPar
each time it appears in the function body).
The entire process involved in the function call shown in Display 4.3 is described in detail in Display 4.4.
A function may return a bool
value. A function that returns a Boolean is called a predicate. Such a function can be used in a Boolean expression to control an if-else
statement or to control a loop statement, or it can be used anywhere else that a Boolean expression is allowed. The returned type for such a function should be the type bool
.
A call to a function that returns a Boolean value of true
or false
can be used anywhere that a Boolean expression is allowed. This can often make a program easier to read. By means of a function declaration, you can associate a complex Boolean expression with a meaningful name and use the name as a Boolean expression in an if-else
statement or anywhere else that a Boolean expression is allowed. For example, the statement
if (((rate >= 10) && (rate < 20)) || (rate == 0))
{
...
}
can be made to read
if (appropriate(rate))
{
...
}
provided that the following function has been defined:
bool appropriate(int rate)
{ return (((rate >= 10) && (rate < 20)) || (rate == 0));
}
You are not required to list formal parameter names in a function declaration. The following two function declarations are equivalent:
double totalCost(int numberPar, double pricePar);
and
double totalCost(int, double);
We will always use the first form so that we can refer to the formal parameters in the comment that accompanies the function declaration. However, you will often see the second form in manuals that describe functions.2
This alternate form applies only to function declarations. Function headers must always list the formal parameter names.
Function declarations are normally placed before the main
part of your program and function definitions are normally placed after the main
part of your program (or, as we will see later in this book, in a separate file). Display 4.6 gives a summary of the syntax for a function declaration and definition. There is actually a bit more freedom than that display indicates. The declarations and executable statements in the function definition can be intermixed, as long as each variable is declared before it is used. The rules about intermixing declarations and executable statements in a function definition are the same as they are for the main
part of a program. However, unless you have reason to do otherwise, it is best to place the declarations first, as indicated in Display 4.6.
Since a function does not return a value until it executes a return
statement, a function must contain one or more return
statements in the body of the function. A function definition may contain more than one return
statement. For example, the body of the code might contain an if-else
statement, and each branch of the if-else
statement might contain a different return
statement, as illustrated in Display 4.5.
Any reasonable pattern of spaces and line breaks in a function definition will be accepted by the compiler. However, you should use the same rules for indenting and laying out a function definition as you use for the main
part of a program. In particular, notice the placement of braces {}
in our function definitions and in Display 4.6. The opening and closing braces that mark the ends of the function body are each placed on a line by themselves. This sets off the function body.
We have discussed where function definitions and function declarations are normally placed. Under normal circumstances these are the best locations for the function declarations and function definitions. However, the compiler will accept programs with the function definitions and function declarations in certain other locations. A more precise statement of the rules is as follows: Each function call must be preceded by either a function declaration for that function or the definition of the function. For example, if you place all of your function definitions before the main
part of the program, then you need not include any function declarations. Knowing this more general rule will help you to understand C++ programs you see in some other books, but you should follow the example of the programs in this book. The style we are using sets the stage for learning how to build your own libraries of functions, which is the style that most C++ programmers use.
The switch
statement and the multiway if-else
statement allow you to place several different statements in each branch. However, doing so can make the switch
statement or if-else
statement difficult to read. Look at the switch
statement in Display 3.7. Each of the branches for choices 1
, 2
, and 3
could be a single function call. This makes the layout of the switch
statement and the overall structure of the program clear. If we had instead placed all the code for each branch in the switch
statement, instead of in the function definitions, then the switch
statement would be an incomprehensible sea of C++ statements. In fact, the switch
statement would not even fit on one screen.
What is the output produced by the following program?
#include <iostream>
using namespace std;
char mystery(int firstPar, int secondPar);
int main()
{
cout << mystery(10, 9) << "ow\n";
return 0;
} char mystery(int firstPar, int secondPar)
{
if (firstPar >= secondPar)
return 'W';
else
return 'H';
}
Write a function declaration and a function definition for a function that takes three arguments, all of type int
, and that returns the sum of its three arguments.
Write a function declaration and a function definition for a function that takes one argument of type int
and one argument of type double
, and that returns a value of type double
that is the average of the two arguments.
Write a function declaration and a function definition for a function that takes one argument of type double
. The function returns the character value 'P'
if its argument is positive and returns 'N'
if its argument is zero or negative.
Carefully describe the call-by-value parameter mechanism.
List the similarities and differences between use of a predefined (that is, library) function and a user-defined function.
Write a function definition for a function called inOrder
that takes three arguments of type int
. The function returns true
if the three arguments are in ascending order; otherwise, it returns false
. For example, inOrder (1, 2, 3)
and inOrder(1, 2, 2)
both return true
, while inOrder(1, 3, 2)
returns false
.
Write a function definition for a function called even
that takes one argument of type int
and returns a bool
value. The function returns true
if its one argument is an even number; otherwise, it returns false
.
Write a function definition for a function isDigit
that takes one argument of type char
and returns a bool
value. The function returns true
if the argument is a decimal digit; otherwise, it returns false
.
Write a function definition for a function isRootOf
that takes two arguments of type int
and returns a bool
value. The function returns true
if the first argument is the square root of the second; otherwise, it returns false
.