One
of the important features in any programming language is the convenient use of
names.
When
you create an object (a variable), you give a name to a region of storage. A
function is a name for an action. By making up names to describe the system at
hand, you create a program that is easier for people to understand and change.
It’s a lot like writing prose – the goal is to communicate with
your readers.
A
problem arises when mapping the concept of nuance in
human language onto a programming language. Often, the same word expresses a
number of different meanings, depending on context. That is, a single word has
multiple meanings – it’s
overloaded.
This is very useful, especially when it comes to trivial differences. You say
“wash the shirt, wash the car.” It would be silly to be forced to
say, “shirt_wash the shirt, car_wash the car” just so the hearer
doesn’t have to make any distinction about the action performed. Most
human languages are redundant, so even if you miss a few words, you can still
determine the meaning. We don’t need unique identifiers – we can
deduce meaning from context.
Most
programming languages, however, require that you have a unique identifier for
each function. If you have three different types of data that you want to print:
int,
char,
and
float,
you generally have to create three different function names, for example,
print_int( ),
print_char( ),
and
print_float( ).
This loads extra work on you as you write the program, and on readers as they
try to understand it.
In
C++, another factor forces the overloading of function names: the constructor.
Because the constructor’s name is predetermined by the name of the class,
it would seem that there can be only one constructor. But what if you want to
create an object in more than one way? For example, suppose you build a class
that can initialize itself in a standard way and also by reading information
from a file. You need two constructors, one that takes no arguments (the
default constructor) and one that takes a
string
as an argument, which is the name of the file to initialize the object. Both
are constructors, so they must have the same name: the name of the class. Thus
function overloading is essential to allow the same function name – the
constructor in this case – to be used with different argument types.
Although
function overloading is a must for constructors, it’s a general
convenience and can be used with any function, not just class member functions.
In addition, function overloading means that if you have two libraries that
contain functions of the same name, they won’t conflict as long as the
argument lists are different. We’ll look at all these factors in detail
throughout this chapter.
The
theme of this chapter is convenient use of function names. Function overloading
allows you to use the same name for different functions, but there’s a
second way to make calling a function more convenient. What if you’d like
to call the same function in different ways? When functions have long argument
lists, it can become tedious to write (and confusing to read) the function
calls when most of the arguments are the same for all the calls. A very
commonly used feature in C++ is called
default
arguments.
A default argument is one the compiler inserts if it isn’t specified in
the function call. Thus the calls
f(“hello”),
f(“hi”,
1)
and
f(“howdy”,
2, ‘c’)
can all be calls to the same function. They could also be calls to three
overloaded functions, but when the argument lists are this similar,
you’ll usually want similar behavior, which calls for a single function.
Function
overloading and default arguments really aren’t very complicated. By the
time you reach the end of this chapter, you’ll understand when to use
them and the underlying mechanisms that implement them during compiling and
linking.