Examine
the two constructors for
Stash( ).
They don’t seem all that different, do they? In fact, the first
constructor seems to be the special case of the second one with the initial
size
set to zero. In this situation it seems a bit of a waste of effort to create
and maintain two different versions of a similar function.
C++
provides a remedy with
default
arguments.
A default argument is a value given in the declaration that the compiler
automatically inserts if you don’t provide a value in the function call.
In the
Stash
example, we can replace the two functions:
Stash(int size); // Zero quantity
Stash(int size, int quantity);
with
the single declaration
Stash(int size, int quantity = 0);
The
Stash(int)
definition is simply removed – all that is necessary is the single
Stash(int,
int)
definition.
Now,
the two object definitions
Stash A(100), B(100, 0);
will
produce exactly the same results. The identical constructor is called in both
cases, but for
A,
the second argument is automatically substituted by the compiler when it sees
the first argument is an
int
and there is no second argument. The compiler has seen the default argument, so
it knows it can still make the function call if it substitutes this second
argument, which is what you’ve told it to do by making it a default.
Default
arguments are a convenience, as function overloading is a convenience. Both
features allow you to use a single name in different situations. The difference
is that the compiler is substituting arguments when you don’t want to put
them in yourself. The preceding example is a good place to use default
arguments instead of function overloading; otherwise you end up with two or
more functions that have similar signatures and similar behaviors. Obviously,
if the functions have very different behaviors, it usually doesn’t make
sense to use default arguments.
There
are two rules you must be aware of when using default arguments. First, only
trailing arguments may be defaulted. That is, you can’t have a default
argument followed by a nondefault argument. Second, once you start using
default arguments, all the remaining arguments must be defaulted. (This follows
from the first rule.)
Default
arguments are only placed in the declaration of a function, which is placed in
a header file.
The compiler must see the default value before it can use it. Sometimes people
will place the commented values of the default arguments in the function
definition, for documentation purposes
void
fn(int x /* = 0 */) { // ...
Default
arguments can make arguments declared without identifiers look a bit funny. You
can end up with
void
f(int x, int = 0, float = 1.1);
In
C++ you don’t need identifiers in the function definition, either:
void
f(int x, int, float f) { /* ... */ }
In
the function body,
x
and
f
can be referenced, but not the middle argument, because it has no name. The
calls must still use a placeholder, though:
f(1)
or
f(1,2,3.0).
This syntax allows you to put the argument in as a placeholder without using
it. The idea is that you might want to change the function definition to use it
later, without changing all the function calls. Of course, you can accomplish
the same thing by using a named argument, but if you define the argument for
the function body without using it, most compilers will give you a warning
message, assuming you’ve made a logical error. By intentionally leaving
the argument name out, you suppress this warning.
More
important, if you start out using a function argument and later decide that you
don’t need it, you can effectively remove it without generating warnings,
and yet not disturb any client code that was calling the previous version of
the function.