命名空間

命名空間（Namespace），也稱名字空間、名稱空間等，它表示著一個標識符（identifier）的可見範圍。一個標識符可在多個命名空間中定義，它在不同命名空間中的含義是互不相干的。這樣，在一個新的命名空間中可定義任何標識符，它們不會與任何已有的標識符發生衝突，因為已有的定義都處於其他命名空間中。

例如，設Bill是X公司的員工，工號為123，而John是Y公司的員工，工號也是123。由於兩人在不同的公司工作，可以使用相同的工號來標識而不會造成混亂，這裡每個公司就表示一個獨立的命名空間。如果兩人在同一家公司工作，工號也相同，便會發生混亂。

這一特點是使用命名空間的主要理由。在大型的電腦程式或文檔中，往往會出現數百或數千個標識符。命名空間提供一隱藏區域標識符的機制。通過將邏輯上相關的標識符組織成相應的命名空間，可使整個系統更加模塊化。

在程式語言中，命名空間是對作用域的一種特殊的抽象，它包含了處於該作用域內的標識符，且本身也用一個標識符來表示，這樣便將一系列在邏輯上相關的標識符用一個標識符組織了起來。許多現代程式語言都支持命名空間。在一些程式語言（例如C++和Python）中，命名空間本身的標識符也屬於一個外層的命名空間，也即命名空間可以嵌套，構成一個命名空間樹，樹根則是無名的全局命名空間。

函數和類的作用域可被視作隱式命名空間，它們和可見性、可訪問性和對象生命周期不可分割的聯繫在一起。

## C++中的命名空間

在C++語言中，命名空間是一種實體（entity），使用namespace來聲明，並使用{ }來界定命名空間體（namespace body）。

例：

```cpp
namespace foo {
    int bar;
}
```

C語言的全局作用域對應於C++全局命名空間作用域。全局命名空間不需要聲明也無法顯式聲明。使用時，可以用前綴為`::`的qualified-id顯式限定全局命名空間作用域中的名稱。例如，`::operator new`指稱全局new運算符函數。

命名空間可以在另一命名空間之中嵌套聲明；但不能聲明在類和代碼塊之中。在命名空間中聲明的名稱，默認具有外部連結屬性（除非聲明的是const對象，它默認是具有內部連結屬性）。

按照是否有名字，可分為有名字的命名空間、不能顯式定義的全局命名空間與匿名命名空間。後者的聲明為：

```cpp
namespace {
    // namespace-body（即声明序列(可选)）
}
```

匿名命名空間中的名字具有文件作用域。這些名字在本編譯單元中可以直接使用；也可以用前綴為`::`的qualified-id顯式限定後使用。匿名命名空間中的名字具有內部連結屬性。

命名空間的成員，是在命名空間體的花括號內聲明了的名字。可以在命名空間體之外，給出命名空間成員的定義。即命名空間的成員聲明與定義可以分開。命名空間內的名字，只能有一次定義，但可以多次聲明。

嵌套的子命名空間必須定義在上層命名空間體之內。禁止把子命名空間的聲明與定義分開。

不能以「命名空間名::成員名;」方式，在命名空間體之外為命名空間添加新成員。必須在命名空間體之中添加新成員的聲明。

可以多次聲明和定義同一命名空間，每次給這一命名空間添加新成員。同名的命名空間即便在聲明位置不同，仍然是同一個實體。

可以在一個命名空間中引入其他命名空間的成員。例如：

```cpp
namespace myNameSpace{
    using namespace His_NameSpace;
    using OLib::List;
    void my_func(String &, List &);
}
```

引用命名空間的成員，有下述辦法：

- 使用命名空間的作用域解析運算符`::`，如：`std::cout`
- `using namespace 命名空间名[::命名空间名…];`這稱為「using-Directive」。該語句使指定的命名空間中的目前已經聲明的名字都提升到當前語句所在的作用域中可直接使用。如果引入的名字與該語句所在的聲明區域的名字發生衝突，則編譯器並不會發出任何警告信息，而只是用聲明區域中的名字自動隱藏（hiding）命名空間中的同名成員，即使`using namespace`更靠後也是如此。
- `using 命名空间名::[命名空间名::……]成员名;` 這稱作「using-declaration」。用於向名字空間或者塊作用域內引入其它名字空間的名字成員；也可用於把基類的非私有成員引入到派生類的定義中，如果引入的是基類的重載函數名字，則把基類的所有同名函數引入，如果派生類有同名同參數同限定的成員函數，則基類的函數被名字隱藏或覆蓋。如果引入的名稱與局部名稱發生同名衝突，編譯器會報錯。

命名空間可以有別名：`namespace 别名 = 命名空间名;` 這使得名字較長的命名空間可以方便地用較短的別名來引用。

C++11起支持內聯命名空間。使用`inline namespace`作為聲明的起始。內聯命名空間的名稱在名稱查找時被特別對待，使用qualified-id引用其中的名稱時，被內聯的命名空間名稱可以省略。也即，內聯命名空間內的標識符被提升到包含著內聯的命名空間的那個父級的命名空間中。內聯命名空間可以在修改命名空間名稱的同時避免在二進位文件中生成的符號改變，因此不同內聯命名空間的名稱可以用於標識接口兼容的不同版本，有助於保持二進位兼容性。這也在標準庫的實現中被使用，如libstdc++和libc++。

```cpp
namespace Contoso
{
    namespace v_10
    {
        template <typename T>
        class Funcs
        {
        public:
            Funcs(void);
            T Add(T a, T b);
            T Subtract(T a, T b);
            T Multiply(T a, T b);
            T Divide(T a, T b);
        };
    }

    inline namespace v_20
    {
        template <typename T>
        class Funcs
        {
        public:
            Funcs(void);
            T Add(T a, T b);
            T Subtract(T a, T b);
            T Multiply(T a, T b);
            std::vector<double> Log(double);
            T Accumulate(std::vector<T> nums);
      };
    }
}
```

## 在XML中的應用

XML雖然不是一個獨立的程式語言，但是它的出現使得命名空間的使用變得更為廣泛。

在同一個命名空間裡，所有的元素名都必須唯一。

聲明一個命名空間使用XML保留的屬性`xmlns`，它的值必須是URI(統一資源標誌符 Uniform Resource Identifier而非URL—Universal Resource Locator)指代.比如`xmlns="http://www.w3.org/1999/xhtml"`.注意，事實上URI是不可讀的，但它對XML解析器來說就只是簡單不過的字串，如 http://www.w3.org/1999/xhtml/ 這個地址本身並不包含任何代碼，它只表示XHTML命名空間.使用URI (比如"http://www.w3.org/1999/xhtml")去標示一個命名空間，而不是用一個簡短的字串 (比如"xhtml")，這樣做是為了減少不同命名空間標示符衝突的可能性.

## 外部連結

- The Use of Name Spaces in Plan 9
- W3C XML Namespace documentation
- XML Namespace Tutorial
- Cover Pages: Namespaces in XML