9.2 Namespaces

I l @ ve RuBoard

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.

I l @ ve RuBoard


Practical C++ Programming
Practical C Programming, 3rd Edition
ISBN: 1565923065
EAN: 2147483647
Year: 2003
Pages: 364

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net