C++名稱空間(namespace)

前言

當隨着C++項目項目的增大,名稱相互衝突的可能性也將增加。使用多個廠商的類庫時,可能導致名稱衝突。例如,兩個類庫可能都定義了名爲List, Tree和Node的類,但定義的方式不兼容。用戶希望使用其中一個庫的List類,而使用另一個庫的Tree類。這種衝突就被稱爲名稱空間問題。

A demo for namespace

namespace Jack {
    double pail;  // 變量聲明
    void fetch;   // 函數原型
    int pal;    // 變量聲明
    struct Well { ... };  // 結構體聲明
}

創建名稱空間的關鍵字是namespace,但是需要注意:

  • 名稱空間可以是全局的,也可以位於另一個名稱空間中(名稱空間的嵌套),但是不能位於代碼塊中。因此,在默認情況下,在名稱空間中聲明的名稱的鏈接性爲外部的。
  • 除了用戶自己定義的名稱空間外,還存在一個全局名稱空間(global namespace)。它對應於文件級聲明區域,因此前面所說的全局變量現在被描述爲位於全局名稱空間中。
  • 任何名稱空間中的名稱都不會與其它名稱空間中的名稱發生衝突。
  • 名稱空間是開放的(open),即可以把名稱加入到已有的名稱空間中。例如,下面的代碼可以將名稱goose加入到Jack已有的名稱列表中:
    namespace Jack {
        char * goose(const char *);
    }

     

通過作用域解析運算符“::”可以訪問給定的名稱空間,如

Jack::pail = 12.34  //使用一個變量
Jack::Hill mole;  //使用一個結構體
Jack::fetch();  //使用一個函數

using聲明和using編譯指令

對於名稱空間內的成員,我們在編寫程序的時候不希望每次使用的時候都顯示地對它們的名稱空間加以限定。對於那些經常用到的部分,如std::cout,如果總是這種寫法的話,將不得已增加程序代碼的數量。對此,C++提供了兩種機制來簡化對名稱空間的使用,它們分別是using聲明和using編譯指令。

using聲明由被限定的名稱和它前面的關鍵字using組成:

using Jack::fetch;  // 一個using聲明

這樣,以後每次再使用fetch,都可以省去其名稱空間。

namespace Jill {
    double bucket(double n) { ... };
    double fetch;
    struct Hill { ... };
}
char fetch;
int main() {
    using Jill::fetch;    // 把fetch加到local的名稱空間中
    double fetch;    // 編譯錯誤!因爲我們已經有一個local的fetch了
    cin >> fetch;    // 輸入Jill::fetch
    cin >> ::fetch;   // 輸入全局的fetch
}

將using添加到局部變量的聲明區域中,避免了將另一個局部變量也命名爲fetch。此外,和其它局部變量一樣,fetch也將覆蓋同名的全局變量。

namespace Jill {
    double bucket(double n) { ... };
    double fetch;
    struct Hill { ... };
}
using Jill::fetch;  // 將fetch加入到全局的名稱空間中
int main() {
    cin >> fetch;    // 輸入Jill::fetch
    cout << fetch;   // 輸出Jill::fetch
}

在全局聲明區域中使用using編譯指令,將使該名稱空間的名稱全局可用,這裏也包括我們非常熟悉的

#include<iostream>
using namespace std;

簡單地說,using聲明使得一個名稱可用,而using編譯指令使所有的名稱都可用。using編譯指令由名稱空間名和它前面的關鍵字using namespace組成,它使名稱空間中的所有名稱都可用,而不需要使用作用域解析運算符。

一般來說,使用using聲明比使用using編譯指令更加安全,這是由於它只導入了特定的名稱,如果發生與局部名稱之間的命名衝突,編譯器會通知我們。而如果使用using編譯指令,將導入所有的名稱,包括可能不需要的名稱,如果與局部變量發生命名從圖,局部名稱很可能覆蓋名稱空間的版本。

名稱空間的其它特性:

嵌套:

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

flame指的是elements::fire::flame,若想使得其名稱可用,可以使用如下聲明:

using namespace elements::fire;

與此同時,也可以在名稱空間中使用using編譯指令和using聲明,如下所示,

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

這種情況下,如果想訪問Jill::fetch,可以使用myth::fetch,也可以使用Jill::fetch,二者等價。

對於using編譯指令來說,嵌套所帶來的影響是傳遞性。即如果A op B 且 B op C, 則 A op C。所以以下語句將導入的名稱空間是myth和elements: 

using namespace myth;

等價於:

using namespace myth;
using namespace elements;

未命名的名稱空間:

可以通過省略名稱空間的名稱來創建未命名的名稱空間:

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

這種定義方法與全局變量類似。然而,由於該名稱空間沒有名稱,因此不能顯式地使用using編譯指令或using聲明來使其在其它地方都可用。具體地說,不能在未命名名稱空間所屬文件之外的其他文件中,使用該名稱空間中的名稱。這提供了鏈接性爲內部的靜態變量的替代品,與static關鍵字的作用相同。因此以上代碼等價於:

static int ice;
static bandycoot;

有關std:

如果開發了一個函數庫或類庫,將其放在一個名稱空間中。事實上,C++提倡將標準函數庫放在名稱空間std中,這種做法擴展到了來自C語言中的函數。例如,頭文件math.h是與C語言兼容的,沒有使用名稱空間,但C++頭文件cmath應將各種數學庫函數方在名稱空間std中。實際上,並非所有的編譯器都完成了這種過渡。正如前面指出的,老式頭文件(如iostream.h)沒有使用名稱空間,但新頭文件iostream使用了std名稱空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章