August 10th Monday

来源:互联网 发布:windows wmi 监控进程 编辑:程序博客网 时间:2024/05/01 11:48

New Namespace Features

What C++ now adds is the ability to create named namespaces by defining a new kind of declarative region, one whose main purpose is to provide an area in which to declare names. The names in one namespace don't conflict with the same names declared in other namespaces, and there are mechanisms for letting other parts of a program use items declared in a namespace. The following code, for example, uses the new keyword namespace to create two namespaces, Jack and Jill.

namespace Jack {
    double pail;
    void fetch();
    int pal;
    struct Well { ... };
}
namespace Jill {
    double bucket(double n) { ... }
    double fetch;
    int pal;
    struct Hill { ... };
}

Namespaces can be located at the global level or inside other namespaces, but they cannot be placed in a block. Thus, a name declared in a namespace has external linkage by default (unless it refers to a constant).

In addition to user-defined namespaces, there is one more namespace, the global namespace. This corresponds to the file-level declarative region, so what used to be termed global variables are now described as being part of the global namespace.

The names in any one namespace can't conflict with names in another namespace. Thus, the fetch in Jack can coexist with the fetch in Jill, and the Hill in Jill can coexist with an external Hill. The rules governing declarations and definitions in a namespace are the same as the rules for global declarations and definitions.

Namespaces are open, meaning you can add names to existing namespaces. For example, the statement

namespace Jill {
    char * goose(const char *);
}

adds the name goose to the existing list of names in Jill.

Similarly, the original Jack namespace provided a prototype for a fetch() function. You can provide the code for the function later in the file (or in another file) by using the Jack namespace again:

namespace Jack {
    void fetch()
    {
        ...
    }
}

Of course, you need a way to access names in a given namespace. The simplest way is to use ::, the scope resolution operator, to qualify a name with its namespace:

Jack::pail = 12.34;  // use a variable
Jill::Hill mole;     // create a type Hill structure
Jack::fetch();       // use a function

An unadorned name, such as pail, is termed the unqualified name, whereas a name with the namespace, as in Jack::pail, is termed a qualified name.

Using-Declarations and Using-Directives

Having to qualify names every time they are used is not an appealing prospect, so C++ provides two mechanisms?the using-declaration and the using-directive?to simplify using namespace names. The using-declaration lets you make particular identifiers available, and the using-directive makes the entire namespace accessible.

The using-declaration consists of preceding a qualified name with the new keyword using:

using Jill::fetch;    // a using-declaration

A using-declaration adds a particular name to the declarative region in which it occurs. For example, a using-declaration of Jill::fetch in main() adds fetch to the declarative region defined by main(). After making this declaration, you can use the name fetch instead of Jill::fetch.

namespace Jill {
    double bucket(double n) { ... }
    double fetch;
    struct Hill { ... };
}
char fetch;
int main()
{
    using Jill::fetch;   // put fetch into local namespace
    double fetch;        // Error! Already have a local fetch
    cin >> fetch;        // read a value into Jill::fetch
    cin >> ::fetch;      // read a value into global fetch
    ...
}

Because a using-declaration adds the name to the local declarative region, this example precludes creating another local variable by the name of fetch. Also, like any other local variable, fetch would override a global variable by the same name.

Placing a using-declaration at the external level adds the name to the global namespace:

void other();
namespace Jill {
    double bucket(double n) { ... }
    double fetch;
    struct Hill { ... };
}
using Jill::fetch;   // put fetch into global namespace
int main()
{
    cin >> fetch;        // read a value into Jill::fetch
    other()
...
}

void other()
{
    cout << fetch;  // display Jill::fetch
...
}

A using-declaration, then, makes a single name available. In contrast, the using-directive makes all the names available. A using-directive consists of preceding a namespace name with the keywords using namespace, and it makes all the names in the namespace available without using the scope resolution operator:

using namespace Jack;  // make all the names in Jack available

Placing a using-directive at the global level makes the namespace names available globally. You've seen this in action many a time:

#include <iostream>    // places names in namespace std
using namespace std;   // make names available globally

Placing a using-directive in a particular function makes the names available just in that function.

int vorn(int m)
{
    using namespace jack; // make names available in vorn()
...
}

Using-Directive Versus Using-Declaration

Using a using-directive to import all the names wholesale is not the same as using multiple using-declarations. It's more like the mass application of a scope resolution operator. When you use a using-declaration, it is as if the name is declared at the location of the using- declaration. If a particular name already is declared in a function, you can't import the same name with a using-declaration. When you use a using-directive, however, name resolution takes place as if you declared the names in the smallest declarative region containing both the using-declaration and the namespace itself. For the following example, that would be the global namespace. If you use a using-directive to import a name that already is declared in a function, the local name will hide the namespace name, just as it would hide a global variable of the same name. However, you still can use the scope resolution operator:

namespace Jill {
    double bucket(double n) { ... }
    double fetch;
    struct Hill { ... };
}
char fetch;                   // global namespace
int main()
{
    using namespace Jill;      // import all namespace names
    Hill Thrill;               // create a type Jill::Hill structure
    double water = bucket(2);  // use Jill::bucket();
    double fetch;              // not an error; hides Jill::fetch
    cin >> fetch;              // read a value into the local fetch
    cin >> ::fetch;            // read a value into global fetch
    cin >> Jill::fetch;        // read a value into Jill::fetch
    ...
}

int foom()
{
    Hill top;                 // ERROR
    Jill::Hill crest;         // valid
}

Here, in main(), the name Jill::fetch is placed in the local namespace. It doesn't have local scope, so it doesn't override the global fetch. But the locally declared fetch hides both Jill::fetch and the global fetch. However, both of the last two fetch variables are available if you use the scope resolution operator. You might want to compare this example to the preceding one, which used a using-declaration.

One other point of note is that although a using-directive in a function treats the namespace names as being declared outside the function, it doesn't make those names available to other functions in the file. Hence in the preceding example, the foom() function can't use the unqualified Hill identifier.

Remember

Suppose a namespace and a declarative region both define the same name. If you attempt to use a using-declaration to bring the namespace name into the declarative region, the two names conflict, and you get an error. If you use a using-directive to bring the namespace name into the declarative region, the local version of the name hides the namespace version.
Generally speaking, the using-declaration is safer to use because it shows exactly what names you are making available. And if the name conflicts with a local name, the compiler lets you know. The using-directive adds all names, even ones you might not need. If a local name conflicts, it overrides the namespace version, and you won't be warned. Also, the open nature of namespaces means that the complete list of names in a namespace might be spread over several locations, making it difficult to know exactly which names you are adding.

What about the approach used for this book's examples?

#include <iostream>
using namespace std;

First, the iostream header file puts everything in the std namespace. Then, the next line exports everything in that namespace into the global namespace. Thus, this approach merely reproduces the pre-namespace era. The main rationale for this approach is expediency. It's easy to do, and if your system doesn't have namespaces, you can replace the preceding two lines with the original form:

#include <iostream.h>

However, the hope of namespace proponents is that you will be more selective and use either the resolution operator or the using-declaration. That is, don't use the following:

using namespace std;  // avoid as too indiscriminate

Instead, do this:

int x;
std::cin >> x;
std::cout << x << std::endl;

Or else do this:

using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;

You can use nested namespaces, as described next, to create a namespace holding the using-declarations you commonly use.

More Namespace Features
You can nest namespace declarations:

namespace elements
{
    namespace fire
    {
        int flame;
        ...
    }
    float water;
}

In this case, you refer to the flame variable as elements::fire::flame. Similarly, you can make the inner names available with this using-directive:

using namespace elements::fire;

Also, you can use using-directives and using-declarations inside namespaces:

namespace myth
{
    using Jill::fetch;
    using namespace elements;
    using std::cout;
    using std::cin;
}

Suppose you want to access Jill::fetch. Because Jill::fetch is now part of the myth namespace, where it can be called fetch, you can access it this way:

std::cin >> myth::fetch;

Of course, because it also is part of the Jill namespace, you still can call it Jill::fetch:

std::cout << Jill::fetch;  // display value read into myth::fetch

Or you can do this, providing no local variables conflict:

using namespace myth;
cin >> fetch;      // really std::cin and Jill::fetch

Now consider applying a using-directive to the myth namespace. The using-directive is transitive. We say an operation op is transitive if A op B and B op C implies A op C. For example, the > operator is transitive. (That is, A bigger than B and B bigger than C implies A bigger than C.) In this context, the upshot is that the statement

using namespace myth;

results in the elements namespace being added via a using-directive also, so it's the same as the following:

using namespace myth;
using namespace elements;

You can create an alias for a namespace. For example, suppose you have a namespace defined as follows:

namespace my_very_favorite_things { ... };

You can make mvft an alias for my_very_favorite_things with the following statement:

namespace mvft = my_very_favorite_things;

You can use this technique to simplify using nested namespaces:

namespace MEF = myth::elements::fire;
using MEF::flame;

Unnamed Namespaces
You can create an unnamed namespace by omitting the namespace name:

namespace        // unnamed namespace
{
    int ice;
    int bandycoot;
}

This behaves as if it were followed by a using-directive; that is, the names declared in this namespace are in potential scope until the end of the declarative region containing the unnamed namespace. In this respect, they are like global variables. However, because the namespace has no name, you can't explicitly use a using-directive or using-declaration to make the names available elsewhere. In particular, you can't use names from an unnamed namespace in a file other than the one containing the namespace declaration. This provides an alternative to using static variables with internal linkage. Indeed, the C++ standard deprecates the use of the keyword static in namespaces and global scope. ("Deprecate" is a term the standard uses to indicate practices that currently are valid but most likely will be rendered invalid by future revisions of the standard.) Suppose, for example, you have this code:

static int counts;   // static storage, internal linkage
int other();
int main()
{
}
int other()
{
}

The intent of the standard is that you should do this instead:

namespace
{
    int counts;   // static storage, internal linkage
}
int other();
int main()
{
}
int other()
{
}

原创粉丝点击