Tuesday, October 11, 2011

C++ Tutorial: Namespace in C++

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.
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
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

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.

Not all namespaces are guaranteed to be unique. Two third-party vendors might inadvertently use the same identifiers for their namespace names.  The following code segment illustrates the  user of Namespace.
1  // programming-techniques tutorial on Namespace
2  // Demonstrating namespaces.
3  #include <iostream>
4  using namespace std; // use std namespace
6  int integer1 = 98; // global variable
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;                                  
16     void printValues(); // prototype                   
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                            
26  // create unnamed namespace                           
27  namespace                                             
28  {                                                     
29     double doubleInUnnamed = 88.22; // declare variable
30  } // end unnamed namespace                            
32  int main()
33  {
34     // output value doubleInUnnamed of unnamed namespace
35     cout << "doubleInUnnamed = " << doubleInUnnamed;
37     // output global variable
38     cout << "\n(global) integer1 = " << integer1;
40     // output values of Example namespace
41     cout << "\nPI = " << Example::PI << "\nE = " << Example::E
42        << "\ninteger1 = " << Example::integer1 << "\nFISCAL3 = "
43        << Example::Inner::FISCAL3 << endl;
45     Example::printValues(); // invoke printValues function
46     return 0;
47  } // end main
49  // display variable and constant values
50  void Example::printValues()
51  {
52     cout << "\nIn printValues:\ninteger1 = " << integer1 << "\nPI = "
53        << PI << "\nE = " << E << "\ndoubleInUnnamed = "
54        << doubleInUnnamed << "\n(global) integer1 = " << ::integer1
55        << "\nFISCAL3 = " << Inner::FISCAL3 << endl;
56  } // end printValues

Using the std Namespace

Line 4 informs the compiler that namespace std is being used. The contents of header file <iostream> 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 above code 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 Namespace

Lines 9, 4 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 27, 30 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.
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 41, 43 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).

No comments:

Post a Comment