"A namespace is a declarative region that provides a scope to the identifiers (the names of types, functions, variables, etc) inside it. Namespaces are used to organize code into logical groups and to prevent name collisions that can occur especially when your code base includes multiple libraries" (Microsoft Documentation).
Chapter 1 introduced the standard namespace, std, and illustrated two options for accessing the I/O classes it declares:
#include <iostream> using namespace std; . . . cout << "hello world" << endl; |
#include <iostream> . . . std::cout << "hello world" << std::endl; |
| (a) | (b) |
All the example programs presented throughout the text and all the programming assignments you have completed depend on the std namespace. Despite using namespaces, we haven't explored how to create them or why they are helpful. This section addresses those omissions.
To understand how namespaces work and why we need them, we must understand what a "name collision" means. Linguists estimate that there are more than a million words in English, with similar numbers in other languages. So, it seems like we should be able to write a program without running out of identifier names. For example, using English's 26-character alphabet, we can form nearly 457,000 four-character patterns, almost half the number of words in the language. But most of those patterns are meaningless. Furthermore, programmers often name programming elements based on their role in a program (i.e., on what they do). This practice sets the stage for potential name collisions or name conflicts.
It's easy to resolve name collisions by changing one of the colliding names when a programming team works together to create a system. But when we create a system incorporating multi-sourced components delivered as executables and object-code libraries (Figure 4, step 3), it's not possible to change conflicting names. I encountered this problem in a C program integrating a database management system with library code licensed by different vendors. In C++, namespaces solve this problem by creating named scopes.
Vendors avoid name collisions by declaring classes, non-member functions, and global variables in uniquely named namespaces with names typically based on corporate names, which are trademarked and, therefore, unique. Namespaces included in the C++ API are managed and reserved by the ANSI C++ committee. In C++, the scope resolution operator binds the namespace name to the names of its enclosed entities. The name "scope resolution operator" suggests that it helps the compiler determine the scope of otherwise ambiguous programming elements.
namespace Acme
{
void print(string name);
};
namespace Widgets
{
void print(string part);
}; |
void Acme::print(string name) { cout << "Acme " << name << endl; }; void Widgets::print(string part) { cout << "Widgets " << part << endl; } |
| (a) | (b) |
int main()
{
Acme::print("Dilbert");
Widgets::print("wing nut");
return 0;
} |
// Error - a namespace is an invalid type name Acme m; Widgets* w = new Widgets; |
| (c) | (d) |
class beta
{
public:
string to_string(int value)
{ return std::to_string(value); }
}; |
namespace std
{
string to_string(int value);
};
|
| (a) | (b) |
string class to_string function (see string To Number and Number To string). Without the namespace name and the scope resolution operator, the call becomes a direct recursion function call. Programmers use the scope resolution operator to resolve the ambiguous use of function names, as illustrated here by to_string.As the size and complexity of programs increase, programmers need some way to organize and manage namespaces. Namespaces are declarative structures like prototypes and class specifications. They are also containers that wrap and extend the names of functions and classes. Programmers incorporate namespaces in header files to wrap prototypes and class specifications. The following examples demonstrate how programmers add namespaces to function prototypes and class specifications in header files.
| Acme.h | Acme.cpp | Client | |
|---|---|---|---|
namespace Acme
{
void f1();
int f2();
}; |
#include "Acme.h"
void Acme::f1()
{
...
}
int Acme::f2()
{
...
} |
#include "Acme.h"
namespace Acme
{
void f1()
{
...
}
int f2()
{
...
}
} |
#include <iostream>
#include "Acme.h"
using namespace std;
using namespace Acme;
int main()
{
f1();
f2();
return 0;
} |
| (a) | (b) | (c) | (d) |
Acme::f1(); and Acme::f2();, rather than employing the "using" statement.#include "Acme.h" directive in the client to include the supplier's path. For example, #include "dir1/dir2/Acme.h" or #include "/acme/Acme.h". Notice that the forward slash character is the path separator, even on Windows platforms. The text revisits and extends pathnames in chapter 14, File System Organization.
| Acme.h | foo.h | bar.h | client |
|---|---|---|---|
namespace Acme
{
class foo;
class bar;
}; |
#include "Acme.h"
/*namespace Acme
{
class foo
{
public:
foo() { ... }
};
};*/
class Acme::foo
{
public:
foo() { ... }
}; |
#include "Acme.h"
/*namespace Acme
{
class bar
{
public:
bar() { ... }
};
};*/
class Acme::bar
{
public:
bar() { ... }
}; |
#include "Acme.h"
#include "foo.h"
#include "bar.h"
using namespace Acme;
int main()
{
foo f;
bar b;
return 0;
} |
| (a) | (b) | (c) | |
class creates a forward declaration for classes foo and bar.Figure 1 illustrates two options for accessing namespace entities. The programming examples appearing throughout the textbook illustrate my preference for option (a), but this choice is not universal. Odumosu Matthew outlines three objections to the using namespace std; option, and extends those argument to the using statement generally.
He recommends, as a better practice, explicitly naming the namespace, as in Figure 1(b), when using an entity, suggesting that the practice will reduce these problems. His objections are worthy of consideration.
The section opens with a definition suggesting that namespaces declare "the names of types, functions, variables, etc." Specifically, namespaces declare classes, non-member functions, and global variables. Figure 2 illustrates namespaces with non-member functions, so the text describes the following examples with classes without loss of generality. I argue that two architectural organizations and one fallback method can realize the same benefits while avoiding excessive coding notations.
The primary way C++ programmers limit class name conflicts is by specifying each class in a separate header file, as demonstrated by the Time and fraction classes. This organization affords programmers considerable namespace control using a familiar mechanism: the #include preprocessor directive. A simple example demonstrates how the directive helps reduce name conflicts.
#include <iostream>
//#include <iomanip> // (i)
#include "setw.h" // (ii)
using namespace std;
int main()
{
cout << setw(20) << "Hello World\n"; // (iii)
return 0;
} |
class setw
{
private:
int wid;
public:
setw(int w) : wid(w) {}
friend ostream& operator<<(ostream& out, setw& w)
{
out.width(w.wid);
return out;
}
}; |
| (a) | (b) - setw.h |
Complex object-oriented programs consist of many objects, instantiated from numerous classes, each contributing to the solution of the program's overall problem. As programs grow in size and complexity, it becomes convenient to organize classes into functional units. An early analysis and design process termed the units components, and named four standard occurrences. Components help illustrate the second architectural organization, helping to reduce name conflicts.
The UML has largely supplanted the Coad & Yourdon analysis and design method. It acknowledges that complex programs often contain more than four functional units. The UML2 (pp. 163-174) renames components as packages, and doesn't specify their number, purpose, or names.
In a program rigorously implemented with the object-oriented paradigm, classes provide sufficiently distinct scopes for most functions and variables. So, adhering to the above architectural organizations significantly reduces the likelihood of name collisions or conflicts in a program. However, they cannot guarantee elimination. If a name conflict occurs in a program, programmers can always fall back to using the fully qualified name consisting of the namespace and the entity names.
namespace Acme
{
class foo;
class dlilbert;
};
namespace Widgets
{
class foo;
class wally;
}; |
#import "/Acme/foo.h"
#import "/Widgets/foo.h"
using namespace Acme;
using namespace Widgets;
int main()
{
Acme::foo f1;
Widgets::foo f2;
dilbert d;
wally w;
return 0;
} |
| (a) | (b) |
Where name conflicts are unequivocally programming errors - they prevent the program from compiling - readability is more subjective. The subjectivity doesn't diminish the importance of readability, but it does make assessing different approaches more challenging: code one programmer finds readable, another may not. std::cout undeniably conveys more information to a reader than cout, but the significance of the additional information is arguable. Programmers with a moderate level of experience know that cout is specified in the std namespace. Classes specified in specialized namespaces appear infrequently in programs and are limited to specific packages. In the case of specialized namespaces, a suitable comment will clarify any ambiguities, while still allowing the program to use a compact notation.
The lifespans of software systems vary significantly, with some systems operating for decades. The longer a system persists, the more likely it is to need maintenance: extension, adaptation, and correction. While readable code is essential for maintainable software systems, maintainability entails more than choosing how programmers access entities specified in a namespace. Maintainability rests on robust, well-designed data structures that isolate and control access to the stored data. It further requires tight, cohesive functional units with minimal inter-unit connections that limit and localize the effects of internal changes. The architectural organizations described above implement these characteristics, enhancing maintainability far more than explicitly naming the namespace specifying an entity.
Programmers working for an institution that has programming standards should follow those standards. Students submitting programs for scoring or evaluation should follow the instructor's standards. In the absence of explicit standards or when programmers create their own product, they create and follow their personal style: whether they favor or disfavor the using statement is a matter of taste.