关于 namespace


namespace用来创建命名空间,或者开启既存的命名空间,因此,若想在其他标头文件或源码位置,往〈简介命名空间〉中既有的bank命名空间添加新的定义,只要指定相同名称就可以了。例如:

namespace bank {
    int x;
    namespace vip {
        class Foo {
            ...
        };

        int x;
    }
    ... 
}

命名空间可以形成嵌套,以上例来说,想使用Foo类的话,必须使用bank::vip::Foo,各个命名空间中可以定义自身范畴中的名称,内部命名空间可以看得到外部命名空间的名称,然而外部命名空间看不到内部定义的名称,如果内部定义了与外部同名的名称,那内部中的名称会覆盖外部的同名名称。

如果标头文件 a.h 定义了命名空间为na,而它被include到另一命名空间中:

namespace nb {
    #include "a.h"
    ...
}

那么na会成为nb内部的命名空间,因此定义在na中的名称,必须使用nb::na指定范畴来访问。

命名空间可以inline,就好比展开在相对应的位置,例如,如果标头文件 bankv1.h 定义了inline命名空间为bankv1

inline namespace bankv1 {
    Account {
        ...
    };
    ...
}

而它被include到另一命名空间中:

namespace bank {
    #include "bankv1.h"
    ...
}

那么bank不会是bank的内部命名空间,而会像是:

namespace bank {
    Account {
        ...
    };
    ...
}

其作用之一就在于,如果哪天定义了bankv2,定义在 bankv2.h:

inline namespace bankv2 {
    Account {
        ...
    };
    ...
}

那么原本的 bankv1.h 可以去除inline

namespace bankv1 {
    Account {
        ...
    };
    ...
}

bank命名空间修改为:

namespace bank {
    #include "bankv2.h"
    #include "bankv1.h"
    ...
}

那么程序中使用到bank::Account的代码,会是 bankv2.h 定义的版本,如果基于兼容性,还想继续使用 bankv1.h 的定义,可以透过bank::bankv1指定范畴来访问。

namespace也可用来为既有的命名空间取别名,例如,为bank::vip取个vip的别名:

namespace vip = bank::vip;

命名空间可以是匿名的,例如,若有个 foo.cpp 实现如下:

namespace {
    int x = 10;
}

void foo() {
    std::cout << x << std::endl;
}

...

匿名的命名空间中定义的名称,具有static的生命周期,在第一次使用到的时候被创建,程序结束之后销毁,因为没有名称,只有与匿名空间同文件、同层次的代码,可以直接访问匿名空间中的名称,以上例来说,foo与匿名空间都是定义于全局命名空间,foo中可以直接访问x

然而,如果有另一个 foo2.cpp 也如下定义了x

namespace {
    int x = 10;
}

void foo2() {
    std::cout << x << std::endl;
}

...

因为只有与匿名空间同文件、同层次的代码,可以直接访问匿名空间中的名称,x仅在 foo2.cpp 中可见,若同时使用到 foo.cpp、foo2.cpp,编译时并不会发生错误,然而拿掉匿名的namespace定义,就会因x重复定义而编译错误。

只有与匿名空间同文件、同层次的代码,可以直接访问匿名空间中的名称,因此如果是以下:

namespace bank {
    namespace {
        int x = 10;
    }
}

x就只有同文件、同样在bank命名空间的代码,才可以直接访问x,若是同文件、非同一命名空间的代码,必须透过bank::x来访问。


展开阅读全文