namespaces

A program includes many identifiers defined in different scopes. Sometimes a variable of one scope will "overlap" (i.e., collide) with a variable of the same name in a different scope, possibly creating a naming conflict. Such overlapping can occur at many levels. Identifier overlapping occurs frequently in third-party libraries that happen to use the same names for global identifiers (such as functions). This can cause compiler errors.

Good Programming Practice 24.1

Avoid identifiers that begin with the underscore character, which can lead to linker errors. Many code libraries use names that begin with underscores.

The C++ standard attempts to solve this problem with namespaces. Each namespace defines a scope in which identifiers and variables are placed. To use a namespace member, either the member's name must be qualified with the namespace name and the binary scope resolution operator (::), as in

MyNameSpace::member

or a using declaration or using directive must appear before the name is used in the program. Typically, such using statements are placed at the beginning of the file in which members of the namespace are used. For example, placing the following using directive at the beginning of a source-code file

using namespace MyNameSpace;

specifies that members of namespace MyNameSpace can be used in the file without preceding each member with MyNameSpace and the scope resolution operator (::).

A using declaration (e.g., using std::cout;) brings one name into the scope where the declaration appears. A using directive (e.g., using namespace std;) brings all the names from the specified namespace into the scope where the directive appears.

Software Engineering Observation 24.1

Ideally, in large programs, every entity should be declared in a class, function, block or namespace. This helps clarify every entity's role.


Error-Prevention Tip 24.2

Precede a member with its namespace name and the scope resolution operator (::) if the possibility exists of a naming conflict.

Not all namespaces are guaranteed to be unique. Two third-party vendors might inadvertently use the same identifiers for their namespace names. Figure 24.2 demonstrates the use of namespaces.

Figure 24.2. Demonstrating the use of namespaces.

(This item is displayed on pages 1204 - 1205 in the print version)

1 // Fig. 24.2: fig24_02.cpp 2 // Demonstrating namespaces. 3 #include 4 using namespace std; // use std namespace 5 6 int integer1 = 98; // global variable 7 8 // create namespace Example 9 namespace Example 10 { 11 // declare two constants and one variable 12 const double PI = 3.14159; 13 const double E = 2.71828; 14 int integer1 = 8; 15 16 void printValues(); // prototype 17 18 // nested namespace 19 namespace Inner 20 { 21 // define enumeration 22 enum Years { FISCAL1 = 1990, FISCAL2, FISCAL3 }; 23 } // end Inner namespace 24 } // end Example namespace 25 26 // create unnamed namespace 27 namespace 28 { 29 double doubleInUnnamed = 88.22; // declare variable 30 } // end unnamed namespace 31 32 int main() 33 { 34 // output value doubleInUnnamed of unnamed namespace 35 cout << "doubleInUnnamed = " << doubleInUnnamed; 36 37 // output global variable 38 cout << " (global) integer1 = " << integer1; 39 40 // output values of Example namespace 41 cout << " PI = " << Example::PI << " E = " << Example::E 42 << " integer1 = " << Example::integer1 << " FISCAL3 = " 43 << Example::Inner::FISCAL3 << endl; 44 45 Example::printValues(); // invoke printValues function 46 return 0; 47 } // end main 48 49 // display variable and constant values 50 void Example::printValues() 51 { 52 cout << " In printValues: integer1 = " << integer1 << " PI = " 53 << PI << " E = " << E << " doubleInUnnamed = " 54 << doubleInUnnamed << " (global) integer1 = " << ::integer1 55 << " FISCAL3 = " << Inner::FISCAL3 << endl; 56 } // end printValues  

doubleInUnnamed = 88.22 (global) integer1 = 98 PI = 3.14159 E = 2.71828 integer1 = 8 FISCAL3 = 1992 In printValues: integer1 = 8 PI = 3.14159 E = 2.71828 doubleInUnnamed = 88.22 (global) integer1 = 98 FISCAL3 = 1992  


Using the std Namespace

Line 4 informs the compiler that namespace std is being used. The contents of header file are all defined as part of namespace std. [Note: Most C++ programmers consider it poor practice to write a using directive such as line 4 because the entire contents of the namespace are included, thus increasing the likelihood of a naming conflict.]

The using namespace directive specifies that the members of a namespace will be used frequently throughout a program. This allows the programmer to access all the members of the namespace and to write more concise statements such as

cout << "double1 = " << double1;

rather than

std::cout << "double1 = " << double1;

Without line 4, either every cout and endl in Fig. 24.2 would have to be qualified with std::, or individual using declarations must be included for cout and endl as in:

using std::cout; using std::endl;

The using namespace directive can be used for predefined namespaces (e.g., std) or programmer-defined namespaces.


Defining Namespaces

Lines 924 use the keyword namespace to define namespace Example. The body of a namespace is delimited by braces ({}). Namespace Example's members consist of two constants (PI and E at lines 1213), an int (integer1 at line 14), a function (printValues at line 16) and a nested namespace (Inner at lines 1923). Notice that member integer1 has the same name as global variable integer1 (line 6). Variables that have the same name must have different scopesotherwise compilation errors occur. A namespace can contain constants, data, classes, nested namespaces, functions, etc. Definitions of namespaces must occupy the global scope or be nested within other namespaces.

Lines 2730 create an unnamed namespace containing the member doubleInUnnamed. The unnamed namespace has an implicit using directive, so its members appear to occupy the global namespace, are accessible directly and do not have to be qualified with a namespace name. Global variables are also part of the global namespace and are accessible in all scopes following the declaration in the file.

Software Engineering Observation 24.2

Each separate compilation unit has its own unique unnamed namespace; i.e., the unnamed namespace replaces the static linkage specifier.

 

Accessing Namespace Members with Qualified Names

Line 35 outputs the value of variable doubleInUnnamed, which is directly accessible as part of the unnamed namespace. Line 38 outputs the value of global variable integer1. For both of these variables, the compiler first attempts to locate a local declaration of the variables in main. Since there are no local declarations, the compiler assumes those variables are in the global namespace.

Lines 4143 output the values of PI, E, integer1 and FISCAL3 from namespace Example. Notice that each must be qualified with Example:: because the program does not provide any using directive or declarations indicating that it will use members of namespace Example. In addition, member integer1 must be qualified, because a global variable has the same name. Otherwise, the global variable's value is output. Notice that FISCAL3 is a member of nested namespace Inner, so it must be qualified with Example::Inner::.

Function printValues (defined at lines 5056) is a member of Example, so it can access other members of the Example namespace directly without using a namespace qualifier. The output statement in lines 5255 outputs integer1, PI, E, doubleInUnnamed, global variable integer1 and FISCAL3. Notice that PI and E are not qualified with Example. Variable doubleInUnnamed is still accessible, because it is in the unnamed namespace and the variable name does not conflict with any other members of namespace Example. The global version of integer1 must be qualified with the unary scope resolution operator (::), because its name conflicts with a member of namespace Example. Also, FISCAL3 must be qualified with Inner::. When accessing members of a nested namespace, the members must be qualified with the namespace name (unless the member is being used inside the nested namespace).

Common Programming Error 24.1

Placing main in a namespace is a compilation error.


Aliases for Namespace Names

Namespaces can be aliased. For example the statement

namespace CPPHTP5E = CPlusPlusHowToProgram5E;

creates the alias CPPHTP5E for CPlusPlusHowToProgram5E.

Категории