9.2 Namespaces
The
size of programs has grown steadily, and as the number of lines of
code in a program grows larger and larger, so does the number of
global variables. As a result, the global namespace has become very
crowded.
One solution is to use stylized variable names. For example, in one
program, all variables in the data processing module would begin with
dp_ and all variables in the storage module would
begin with st_.
This works after a fashion, but things get a little hairy as more and
more modules are added. When you're dealing with the
core software system, the user interface module, the game group, and
the backgammon module, and you have to prefix all your variables with
core_ui_games_back_, things have gotten out of
hand.
The C++ solution to this problem is to divide the program into
namespaces. You deal with namespaces every day in real life. For
example, chances are that you refer to the members of your family by
their first names, such as Steve, Bill, Sandra, and Fred. Someone
outside the family would use more formal names, like Steve Smith,
Bill Smith, and so on.
C++ lets you define something called a namespace. All the variables declared inside a
namespace
are
considered to be members of the same family, or
namespace. For example, the following code
declares three integers that are members of the namespace
display:
namespace display {
int width; // The width of the display
int height; // Height of the display in lines
bool visible; // Is the display visible?
};
A family member's full name might be Stephen Douglas
Smith. The C++ equivalent of a full name is something called a fully
qualified name. In this case, the fully qualified name of the
variable width is
display::width. Functions that belong to the
family (i.e., functions that are part of the namespace
display) can use the less formal name
width.
9.2.1 Namespace std
We
started out using the object std::cout for output.
What this actually means is that we are using the variable
cout in the namespace std. This
namespace is used by C++ to define its standard library objects and
functions.
You may remember that we began most of our programs with the
statement:
#include <iostream>
The first statement causes the compiler to read in a file called
iostream, which contains the definitions of the
C++ standard variables. For example, a simplified
iostream might look like this:
namespace std {
istream cin; // Define the input stream cin
ostream cout; // Define the output stream cout
ostream cerr; // Define the standard error stream
// Lots of other stuff
}
Once the compiler has seen these definitions,
std::cin, std::cout, and
std::cerr are available for our use.
9.2.2 Global Namespace
If
you do not enclose your code or variables in any namespace, a blank
namespace is assigned to them. For example, the expression:
::global = 45;
assigns 45 to the variable global, which was
declared outside any namespace declaration.
9.2.3 File-Specific Namespace
Let's suppose you
want to define a module and you want most of the functions and
variables in the file to exist in their own unique namespace.
You could put the following statement at the top of your file:
namespace my_file_vars {
But what happens if, by some strange quirk of fate, someone else
defines a namespace with the same name? The result is a namespace
collision.
To avoid this, C++ has invented the unnamed namespace. The
declaration:
namespace {
with no name specified, puts all the enclosed declarations in a
namespace unique to the file.
9.2.4 Nested Namespaces
Namespaces
may be nested. For example, we could declare some variables as
follows:
namespace core {
namespace games {
namespace dice {
int roll; // The value of the last roll
Nesting this deep is a little verbose
(core::games::dice::roll), but if you have a lot
of code to organize, nested namespaces may be useful.
9.2.5 The using Statement
Let's assume we
have a program with a command module (with the namespace
command) and a command parsing module (with the
namespace command_parser). These two modules are
very closely related, and the command module makes frequent
references to variables inside the parsing module.
(We'll also ignore the fact that tight coupling like
this is a bad design.)
When writing the command module, if you want to refer to a variable
in the parsing module, you have to prefix it with the namespace
identifier:
if (command_parser::first_argument == "ShowAll")
Because these modules are tightly coupled, you have to write out
command_parser::first_argument a lot of times.
This can get tiring after a while.
But you can tell C++, "I know that
first_argument is in the
command_parser module, but pretend that
it's in mine too." This is
accomplished through the using
statement:
using command_parser::first_argument;
C++ will now let you use the name first_argument
instead of command_parser::first_argument.
using command_parser::first_argument;
if (first_argument == "ShowAll")
 |
The scope of a using declaration is
the same as any other variable declaration. It ends at the end of the
block in which it is declared.
|
|
Now let's suppose there are a lot of variables that
we wish to import from the module command_parser.
We could put a using statement in
our code for each one, but this would require a lot of statements. Or
we can do things wholesale and tell C++ that all the names in the
namespace command_parser are to be imported into
our module. This is done with the statement:
using namespace command_parser;
9.2.5.1 The problem with the using statement
The use of the using statement
should be avoided in most cases. The example we presented here had
many interconnects between the two namespaces which necessitated the
use of the using statement. But
it's considered bad program design to have so many
interconnects.
The using statement also causes
namespace confusion. Normally if you see a variable without a scope
declaration (e.g., signal_curve) you can assume it
belongs to the current namespace. If there are using statements in the program, this
assumption is no longer valid and your life just got more complex.
Programs are complex enough already, and this complication is not
welcome.
|