Item 1: Prefer const and inline to #define.
This Item might better be called "prefer the compiler to the preprocessor," because #define is often treated as if it's not part of the language per se. That's one of its problems. When you do something like this,
#define ASPECT_RATIO 1.653
the symbolic name ASPECT_RATIO may never be seen by compilers; it may be removed by the preprocessor before the source code ever gets to a compiler. As a result, the name ASPECT_RATIO may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to 1.653, not ASPECT_RATIO. If ASPECT_RATIO was defined in a
header file you didn't write, you'd then have no idea where that 1.653 came from, and you'd probably waste time
tracking it down. This problem can also crop up in a symbolic debugger, because, again, the name you're
programming with may not be in the symbol table.
The solution to this sorry scenario is simple and succinct. Instead of using a preprocessor macro, define a
constant:
const double ASPECT_RATIO = 1.653;
This approach works like a charm. There are two special cases worth mentioning, however.
First, things can get a bit tricky when defining constant pointers. Because constant definitions are typically put
in header files (where many different source files will include them), it's important that the
pointer
be declared
const, usually in addition to what the pointer points to. To define a constant char*-based string in a header file,
for example, you have to write const
twice
:
const char * const authorName = "Scott Meyers";
For a discussion of the meanings and uses of const, especially in conjunction with pointers, see Item 21
.
Second, it's often convenient to define class-specific constants, and that calls for a slightly different tack. To
limit the scope of a constant to a class, you must make it a member, and to ensure there's at most one copy of the
constant, you must make it a static member:
class GamePlayer {
private:
static const int NUM_TURNS = 5; // constant declaration
int scores[NUM_TURNS]; // use of constant
...
};
There's a minor wrinkle, however, which is that what you see above is a declaration for NUM_TURNS, not a definition. You must still define static class members in an implementation file:
const int GamePlayer::NUM_TURNS; // mandatory definition;
// goes in class impl. file
There's no need to lose sleep worrying about this detail. If you forget the definition, your linker should remind
you.
Older compilers may not accept this syntax, because it used to be illegal to provide an initial value for a static
class member at its point of declaration. Furthermore, in-class initialization is allowed only for integral types
(e.g., ints, bools, chars, etc.), and only for constants. In cases where the above syntax can't be used, you put the
initial value at the point of definition: