關於 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 來存取。