You are on page 1of 10

should avoid using register because the compiler can usually do a

better job of optimization than you.

Namespaces
Although names can be nested inside classes, the names of global
functions, global variables, and classes are still in a single global
name space. The static keyword gives you some control over this
by allowing you to give variables and functions internal linkage
(that is, to make them file static). But in a large project, lack of
control over the global name space can cause problems. To solve
these problems for classes, vendors often create long complicated
names that are unlikely to clash, but then you’re stuck typing those
names. (A typedef is often used to simplify this.) It’s not an elegant,
language-supported solution.

You can subdivide the global name space into more manageable
pieces using the namespace feature of C++. The namespace
keyword, similar to class, struct, enum, and union, puts the names
of its members in a distinct space. While the other keywords have
additional purposes, the creation of a new name space is the only
purpose for namespace.

Creating a namespace
The creation of a namespace is notably similar to the creation of a
class:

//: C10:MyLib.cpp
namespace MyLib {
// Declarations
}
int main() {} ///:~

This produces a new namespace containing the enclosed


declarations. There are significant differences from class, struct,
union and enum, however:

10: Name Control 437


• A namespace definition can appear only at global scope, or
nested within another namespace.

• No terminating semicolon is necessary after the closing brace


of a namespace definition.

• A namespace definition can be “continued” over multiple


header files using a syntax that, for a class, would appear to
be a redefinition:

//: C10:Header1.h
#ifndef HEADER1_H
#define HEADER1_H
namespace MyLib {
extern int x;
void f();
// ...
}

#endif // HEADER1_H ///:~


//: C10:Header2.h
#ifndef HEADER2_H
#define HEADER2_H
#include "Header1.h"
// Add more names to MyLib
namespace MyLib { // NOT a redefinition!
extern int y;
void g();
// ...
}

#endif // HEADER2_H ///:~


//: C10:Continuation.cpp
#include "Header2.h"
int main() {} ///:~

• A namespace name can be aliased to another name, so you


don’t have to type an unwieldy name created by a library
vendor:

//: C10:BobsSuperDuperLibrary.cpp
namespace BobsSuperDuperLibrary {
class Widget { /* ... */ };

438 Thinking in C++ www.BruceEckel.com


class Poppit { /* ... */ };
// ...
}
// Too much to type! I’ll alias it:
namespace Bob = BobsSuperDuperLibrary;
int main() {} ///:~

• You cannot create an instance of a namespace as you can


with a class.

Unnamed namespaces
Each translation unit contains an unnamed namespace that you can
add to by saying “namespace” without an identifier:

//: C10:UnnamedNamespaces.cpp
namespace {
class Arm { /* ... */ };
class Leg { /* ... */ };
class Head { /* ... */ };
class Robot {
Arm arm[4];
Leg leg[16];
Head head[3];
// ...
} xanthan;
int i, j, k;
}
int main() {} ///:~

The names in this space are automatically available in that


translation unit without qualification. It is guaranteed that an
unnamed space is unique for each translation unit. If you put local
names in an unnamed namespace, you don’t need to give them
internal linkage by making them static.

C++ deprecates the use of file statics in favor of the unnamed


namespace.

Friends
You can inject a friend declaration into a namespace by declaring it
within an enclosed class:

10: Name Control 439


//: C10:FriendInjection.cpp
namespace Me {
class Us {
//...
friend void you();
};
}
int main() {} ///:~

Now the function you( ) is a member of the namespace Me.

If you introduce a friend within a class in the global namespace, the


friend is injected globally.

Using a namespace
You can refer to a name within a namespace in three ways: by
specifying the name using the scope resolution operator, with a
using directive to introduce all names in the namespace, or with a
using declaration to introduce names one at a time.

Scope resolution
Any name in a namespace can be explicitly specified using the
scope resolution operator in the same way that you can refer to the
names within a class:

//: C10:ScopeResolution.cpp
namespace X {
class Y {
static int i;
public:
void f();
};
class Z;
void func();
}
int X::Y::i = 9;
class X::Z {
int u, v, w;
public:
Z(int i);
int g();

440 Thinking in C++ www.BruceEckel.com


};
X::Z::Z(int i) { u = v = w = i; }
int X::Z::g() { return u = v = w = 0; }
void X::func() {
X::Z a(1);
a.g();
}
int main(){} ///:~

Notice that the definition X::Y::i could just as easily be referring to a


data member of a class Y nested in a class X instead of a namespace
X.

So far, namespaces look very much like classes.

The using directive


Because it can rapidly get tedious to type the full qualification for
an identifier in a namespace, the using keyword allows you to
import an entire namespace at once. When used in conjunction
with the namespacekeyword this is called a using directive. The
using directive makes names appear as if they belong to the nearest
enclosing namespace scope, so you can conveniently use the
unqualified names. Consider a simple namespace:

//: C10:NamespaceInt.h
#ifndef NAMESPACEINT_H
#define NAMESPACEINT_H
namespace Int {
enum sign { positive, negative };
class Integer {
int i;
sign s;
public:
Integer(int ii = 0)
: i(ii),
s(i >= 0 ? positive : negative)
{}
sign getSign() const { return s; }
void setSign(sign sgn) { s = sgn; }
// ...
};

10: Name Control 441


}
#endif // NAMESPACEINT_H ///:~

One use of the using directive is to bring all of the names in Int into
another namespace, leaving those names nested within the
namespace:

//: C10:NamespaceMath.h
#ifndef NAMESPACEMATH_H
#define NAMESPACEMATH_H
#include "NamespaceInt.h"
namespace Math {
using namespace Int;
Integer a, b;
Integer divide(Integer, Integer);
// ...
}
#endif // NAMESPACEMATH_H ///:~

You can also declare all of the names in Int inside a function, but
leave those names nested within the function:

//: C10:Arithmetic.cpp
#include "NamespaceInt.h"
void arithmetic() {
using namespace Int;
Integer x;
x.setSign(positive);
}
int main(){} ///:~

Without the using directive, all the names in the namespace would
need to be fully qualified.

One aspect of the using directive may seem slightly


counterintuitive at first. The visibility of the names introduced with
a using directive is the scope in which the directive is made. But
you can override the names from the using directive as if they’ve
been declared globally to that scope!

//: C10:NamespaceOverriding1.cpp
#include "NamespaceMath.h"

442 Thinking in C++ www.BruceEckel.com


int main() {
using namespace Math;
Integer a; // Hides Math::a;
a.setSign(negative);
// Now scope resolution is necessary
// to select Math::a :
Math::a.setSign(positive);
} ///:~

Suppose you have a second namespace that contains some of the


names in namespace Math:

//: C10:NamespaceOverriding2.h
#ifndef NAMESPACEOVERRIDING2_H
#define NAMESPACEOVERRIDING2_H
#include "NamespaceInt.h"
namespace Calculation {
using namespace Int;
Integer divide(Integer, Integer);
// ...
}
#endif // NAMESPACEOVERRIDING2_H ///:~

Since this namespace is also introduced with a using directive, you


have the possibility of a collision. However, the ambiguity appears
at the point of use of the name, not at the using directive:

//: C10:OverridingAmbiguity.cpp
#include "NamespaceMath.h"
#include "NamespaceOverriding2.h"
void s() {
using namespace Math;
using namespace Calculation;
// Everything's ok until:
//! divide(1, 2); // Ambiguity
}
int main() {} ///:~

Thus, it’s possible to write using directives to introduce a number


of namespaces with conflicting names without ever producing an
ambiguity.

10: Name Control 443


The using declaration
You can inject names one at a time into the current scope with a
using declaration. Unlike the using directive, which treats names as
if they were declared globally to the scope, a using declaration is a
declaration within the current scope. This means it can override
names from a using directive:

//: C10:UsingDeclaration.h
#ifndef USINGDECLARATION_H
#define USINGDECLARATION_H
namespace U {
inline void f() {}
inline void g() {}
}
namespace V {
inline void f() {}
inline void g() {}
}
#endif // USINGDECLARATION_H ///:~

//: C10:UsingDeclaration1.cpp
#include "UsingDeclaration.h"
void h() {
using namespace U; // Using directive
using V::f; // Using declaration
f(); // Calls V::f();
U::f(); // Must fully qualify to call
}
int main() {} ///:~

The using declaration just gives the fully specified name of the
identifier, but no type information. This means that if the
namespace contains a set of overloaded functions with the same
name, the using declaration declares all the functions in the
overloaded set.

You can put a using declaration anywhere a normal declaration can


occur. A using declaration works like a normal declaration in all
ways but one: because you don’t give an argument list, it’s possible
for a using declaration to cause the overload of a function with the
same argument types (which isn’t allowed with normal

444 Thinking in C++ www.BruceEckel.com


overloading). This ambiguity, however, doesn’t show up until the
point of use, rather than the point of declaration.

A using declaration can also appear within a namespace, and it has


the same effect as anywhere else – that name is declared within the
space:

//: C10:UsingDeclaration2.cpp
#include "UsingDeclaration.h"
namespace Q {
using U::f;
using V::g;
// ...
}
void m() {
using namespace Q;
f(); // Calls U::f();
g(); // Calls V::g();
}
int main() {} ///:~

A using declaration is an alias, and it allows you to declare the


same function in separate namespaces. If you end up re-declaring
the same function by importing different namespaces, it’s OK –
there won’t be any ambiguities or duplications.

The use of namespaces


Some of the rules above may seem a bit daunting at first, especially
if you get the impression that you’ll be using them all the time. In
general, however, you can get away with very simple usage of
namespaces as long as you understand how they work. The key
thing to remember is that when you introduce a global using
directive (via a “using namespace” outside of any scope) you have
thrown open the namespace for that file. This is usually fine for an
implementation file (a “cpp” file) because the using directive is
only in effect until the end of the compilation of that file. That is, it
doesn’t affect any other files, so you can adjust the control of the
namespaces one implementation file at a time. For example, if you
discover a name clash because of too many using directives in a

10: Name Control 445


particular implementation file, it is a simple matter to change that
file so that it uses explicit qualifications or using declarations to
eliminate the clash, without modifying other implementation files.

Header files are a different issue. You virtually never want to


introduce a global using directive into a header file, because that
would mean that any other file that included your header would
also have the namespace thrown open (and header files can include
other header files).

So, in header files you should either use explicit qualification or


scoped using directives and using declarations. This is the practice
that you will find in this book, and by following it you will not
“pollute” the global namespace and throw yourself back into the
pre-namespace world of C++.

Static members in C++


There are times when you need a single storage space to be used by
all objects of a class. In C, you would use a global variable, but this
is not very safe. Global data can be modified by anyone, and its
name can clash with other identical names in a large project. It
would be ideal if the data could be stored as if it were global, but be
hidden inside a class, and clearly associated with that class.

This is accomplished with static data members inside a class. There


is a single piece of storage for a static data member, regardless of
how many objects of that class you create. All objects share the
same static storage space for that data member, so it is a way for
them to “communicate” with each other. But the static data belongs
to the class; its name is scoped inside the class and it can be public,
private, or protected.

Defining storage for static data members


Because static data has a single piece of storage regardless of how
many objects are created, that storage must be defined in a single

446 Thinking in C++ www.BruceEckel.com

You might also like