Defining variables on the fly
As noted earlier in this chapter, there
is a significant difference between C and C++ when defining
variables.
Both languages require that variables be defined before they are used, but C
(and many other traditional procedural languages) forces you to define all the
variables at the beginning of a scope, so that when the compiler creates a block
it can allocate space for those variables.
While reading C code, a block of variable
definitions is usually the first thing you see when entering a scope. Declaring
all variables at the beginning of the block requires the
programmer to write in a particular way because of the implementation details of
the language. Most people don’t know all the variables they are going to
use before they write the code, so they must keep jumping back to the beginning
of the block to insert new variables, which is awkward and causes errors. These
variable definitions don’t usually mean much to the reader, and they
actually tend to be confusing because they appear apart from the context in
which they are used.
C++ (not C) allows you to define
variables anywhere in a scope, so you can define a
variable right before you use it. In addition, you can initialize the variable
at the point you define it, which prevents a certain class of errors. Defining
variables this way makes the code much easier to write and reduces the errors
you get from being forced to jump back and forth within a scope. It makes the
code easier to understand because you see a variable defined in the context of
its use. This is especially important when you are defining and initializing a
variable at the same time – you can see the meaning of the initialization
value by the way the variable is used.
You can also define variables inside the
control expressions of for loops and
while loops, inside the conditional of an
if statement, and inside the selector statement of
a switch. Here’s an example showing
on-the-fly variable definitions:
//: C03:OnTheFly.cpp
// On-the-fly variable definitions
#include <iostream>
using namespace std;
int main() {
//..
{ // Begin a new scope
int q = 0; // C requires definitions here
//..
// Define at point of use:
for(int i = 0; i < 100; i++) {
q++; // q comes from a larger scope
// Definition at the end of the scope:
int p = 12;
}
int p = 1; // A different p
} // End scope containing q & outer p
cout << "Type characters:" << endl;
while(char c = cin.get() != 'q') {
cout << c << " wasn't it" << endl;
if(char x = c == 'a' || c == 'b')
cout << "You typed a or b" << endl;
else
cout << "You typed " << x << endl;
}
cout << "Type A, B, or C" << endl;
switch(int i = cin.get()) {
case 'A': cout << "Snap" << endl; break;
case 'B': cout << "Crackle" << endl; break;
case 'C': cout << "Pop" << endl; break;
default: cout << "Not A, B or C!" << endl;
}
} ///:~
In the innermost scope, p is
defined right before the scope ends, so it is really a useless gesture (but it
shows you can define a variable anywhere). The p in the outer scope is in
the same situation.
The definition of i in the control
expression of the for loop is an example of being able to define a
variable exactly at the point you need it (you can do this only in C++).
The scope of i is the scope of the expression controlled by the
for loop, so you can turn around and re-use i in the next
for loop. This is a convenient and commonly-used idiom in C++; i
is the classic name for a loop counter and you don’t have to keep
inventing new names.
Although the example also shows variables
defined within while, if, and switch statements, this kind
of definition is much less common than those in for expressions, possibly
because the syntax is so constrained. For example, you cannot have any
parentheses. That is, you cannot say:
while((char c = cin.get()) != 'q')
The addition of the extra parentheses
would seem like an innocent and useful thing to do, and because you cannot use
them, the results are not what you might like. The problem occurs because
‘!=’ has a higher precedence than ‘=’, so
the char c ends up containing a bool converted to
char. When that’s printed, on many terminals you’ll see a
smiley-face character.
In general, you can consider the ability
to define variables within while, if, and switch statements
as being there for completeness, but the only place you’re likely to use
this kind of variable definition is in a for loop (where you’ll use
it quite
often).