Safety consts
The use of
const is not limited to replacing #defines
in constant expressions. If you initialize a variable with a value that is
produced at runtime and you know it will not change for the lifetime of that
variable, it is good programming practice to make it a const so the
compiler will give you an error message if you accidentally try to change it.
Here’s an example:
//: C08:Safecons.cpp
// Using const for safety
#include <iostream>
using namespace std;
const int i = 100; // Typical constant
const int j = i + 10; // Value from const expr
long address = (long)&j; // Forces storage
char buf[j + 10]; // Still a const expression
int main() {
cout << "type a character & CR:";
const char c = cin.get(); // Can't change
const char c2 = c + 'a';
cout << c2;
// ...
} ///:~
You can see that i is a
compile-time const, but j is calculated from i. However,
because i is a const, the calculated value
for j still comes from a constant expression and is itself a compile-time
constant. The very next line requires the address of j and therefore
forces the compiler to allocate storage for j. Yet this doesn’t
prevent the use of j in the determination of the size of buf
because the compiler knows j is const and that the value is valid
even if storage was allocated to hold that value at some point in the
program.
In main( ), you see a
different kind of const in the identifier c because the value
cannot be known at compile time. This means storage is required, and the
compiler doesn’t attempt to keep anything in its symbol table (the same
behavior as in C). The initialization must still happen at the point of
definition, and once the initialization occurs, the value cannot be changed. You
can see that c2 is calculated from c and also that scoping works
for consts as it does for any other type –
yet another improvement over the use of #define.
As a matter of practice, if you think a
value shouldn’t change, you should make it a const. This not only
provides insurance against inadvertent changes, it also allows the compiler to
generate more efficient code by eliminating storage and memory reads.