[ Pobierz całość w formacie PDF ]
to some container library.
Large-Scale Projects Are More Susceptible to Name Clashes
Name-clashes are not confined to third party software libraries. In large-scale software projects, short and elegant names for
classes, functions, and constants can also cause name conflicts because it is likely that the same name might be used more than
once to indicate different entities by different developers. In the pre-namespace era, the only workaround was to use various
affixes in identifiers' names. This practice, however, is tedious and error prone. Consider the following:
class string // short but dangerous. someone else may have picked //this name
already...
{
//...
};
class excelSoftCompany_string // a long name is safer but tedious. //A nightmare if
company changes its name...
{
//...
};
Namespaces enable you to use convenient, short, and intelligible names safely. Instead of repeating the unwieldy affixes time
after time, you can group your declarations in a namespace and factor out the recurring affix as follows:
//file excelSoftCompany.h
namespace excelSoftCompany { // a namespace definition
file:///D|/Cool Stuff/old/ftp/1/1/ch08/ch08.htm (2 von 11) [12.05.2000 14:46:20]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 8 - Namespaces
class string {/*..*/};
class vector {/*..*/};
}
Namespace members, like class members, can be defined separately from their declarations. For example
#include
using namespace std;
namespace A
{
void f(); //declaration
}
void A::f() //definition in a separate file
{
cout
}
int main()
{
A::f();
return 0;
}
Properties of Namespaces
Namespaces are more than just name containers. They were designed to allow fast and simple migration of legacy code
without inflicting any overhead. Namespaces have several properties that facilitate their usage. The following sections discuss
these properties.
Fully Qualified Names
A namespace is a scope in which declarations and definitions are grouped together. In order to refer to any of these from
another scope, a fully qualified name is required. A fully qualified name of an identifier consists of its namespaces, followed
by a scope resolution operator (::), its class name, and, finally, the identifier itself. Because both namespaces and classes can
be nested, the resulting name can be rather long -- but it ensures unique identification:
unsigned int maxPossibleLength =
std::string::npos; //a fully qualified name. npos is a member of string; //string
belongs to namespace std
int *p = ::new int; //distinguish global new from overloaded new
However, repeating the fully qualified name is tedious and less readable. Instead, you can use a using declaration or a using
directive.
A using Declaration and a using Directive
A using declaration consists of the keyword using, followed by a namespace::member. It instructs the compiler to locate
every occurrence of a certain identifier (type, operator, function, constant, and so on) in the specified namespace, as if the fully
qualified name were supplied. For example
#include //STL vector; defined in namespace std
int main()
{
using std::vector; //using declaration; every occurrence of vector //is looked up
file:///D|/Cool Stuff/old/ftp/1/1/ch08/ch08.htm (3 von 11) [12.05.2000 14:46:20]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 8 - Namespaces
in std
vector vi;
return 0;
}
A using directive, on the other hand, renders all the names of a specified namespace accessible in the scope of the directive.
It consists of the following sequence: using namespace, followed by a namespace name. For example
#include // belongs to namespace std
#include //iostream classes and operators are also in namespace std
int main()
{
using namespace std; // a using-directive; all and
//declarations now accessible
vector vi;
vi.push_back(10);
cout
return 0;
}
Look back at the string class example (the code is repeated here for convenience):
//file excelSoftCompany.h
namespace excelSoftCompany
{
class string {/*..*/};
class vector {/*..*/};
}
You can now access your own string class as well as the standard string class in the same program as follows:
#include // std::string
#include "excelSoftCompany.h"
int main()
{
using namespace excelSoftCompany;
string s; //referring to class excelSoftCompany::string
std::string standardstr; //now instantiate an ANSI string
return 0;
}
Namespaces Can Be Extended
The C++ standardization committee was well aware of the fact that related declarations can span across several translation
units. Therefore, a namespace can be defined in parts. For example
//file proj_const.h
namespace MyProj
{
enum NetProtocols
{
TCP_IP,
HTTP,
UDP
}; // enum
file:///D|/Cool Stuff/old/ftp/1/1/ch08/ch08.htm (4 von 11) [12.05.2000 14:46:20]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 8 - Namespaces
}
//file proj_classes.h
namespace MyProj
{ // extending MyProj namespace
class RealTimeEncoder{ public: NetProtocols detect(); };
class NetworkLink {}; //global
class UserInterface {};
}
In a separate file, the same namespace can be extended with additional declarations.
The complete namespace MyProj can be extracted from both files as follows:
//file app.cpp
#include "proj_const.h"
#include "proj_classes.h"
int main()
{
using namespace MyProj;
RealTimeEncoder encoder;
NetProtocols protocol = encoder.detect();
return 0;
}
Namespace Aliases
As you have observed, choosing a short name for a namespace can eventually lead to a name clash. However, very long
namespaces are not easy to use. For this purpose, a namespace alias can be used. The following example defines the alias ESC
for the unwieldy Excel_Software_Company namespace. Namespace aliases have other useful purposes, as you will see
soon.
//file decl.h
namespace Excel_Software_Company
{
class Date {/*..*/};
class Time {/*..*/};
}
//file calendar.cpp
#include "decl.h"
int main()
{
namespace ESC = Excel_Software_Company; //ESC is an alias for
// Excel_Software_Company
ESC::Date date;
ESC::Time time;
return 0;
}
Koenig Lookup
Andrew Koenig, one of the creators of C++, devised an algorithm for resolving namespace members' lookup. This algorithm,
also called argument dependent lookup, is used in all standard-compliant compilers to handle cases such as the following:
CAUTION: Please note that some existing compilers do not yet fully support Koenig lookup. Consequently, the
following programs -- which rely on Koenig lookup -- might not compile under compilers that are not fully
file:///D|/Cool Stuff/old/ftp/1/1/ch08/ch08.htm (5 von 11) [12.05.2000 14:46:20]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 8 - Namespaces
compliant to the ANSI/ISO standard in this respect.
namespace MINE
{
class C {};
void func;
}
MINE::C c; // global object of type MINE::C
int main()
{
func( c ); // OK, MINE::f called
return 0;
}
Neither a using declaration nor a using directive exists in the program. Still, the compiler did the right thing -- it correctly
identified the unqualified name func as the function declared in namespace MINE by applying Koenig lookup.
Koenig lookup instructs the compiler to look not just at the usual places, such as the local scope, but also at the namespace that
contains the argument's type. Therefore, in the following source line, the compiler detects that the object c, which is the
argument of the function func(), belongs to namespace MINE. Consequently, the compiler looks at namespace MINE to
locate the declaration of func(), "guessing" the programmer's intent:
func( c ); // OK, MINE::f called
Without Koenig lookup, namespaces impose an unacceptable tedium on the programmer, who has to either repeatedly specify
the fully qualified names or use numerous using declarations. To push the argument in favor of Koenig lookup even further,
consider the following example:
#include
using std::cout;
int main()
{
cout
return 0;
}
The using declaration injects std::cout into the scope of main(), thereby enabling the programmer to use the
nonqualified name cout. However, the overloaded
friend function that is defined in namespace std, and which takes a std::ostream object as its argument. Without Koenig
[ Pobierz całość w formacie PDF ]