C++ 程式設計
名稱空間關鍵字允許您建立一個新的作用域。名稱是可選的,可以省略以建立未命名名稱空間。建立名稱空間後,您必須顯式引用它或使用using關鍵字。名稱空間由namespace塊定義。
- 語法
namespace name {
declaration-list;
}
在許多程式語言中,名稱空間是識別符號的上下文。C++ 可以處理語言中的多個名稱空間。透過使用namespace(或using namespace關鍵字),可以提供一種乾淨的方式將程式碼聚合到一個共享的標籤下,以防止命名衝突或僅僅為了方便回憶和使用非常特定的作用域。除了“名稱空間”之外,還有其他“名稱空間”;這可能會令人困惑。
名稱空間(注意那裡的空格),正如我們將看到的,透過提供一種簡單的方法來區分正在呼叫/使用什麼,超越了作用域的概念。正如我們將看到的,類也是名稱空間,但它們不是名稱空間。
- 示例
namespace foo {
int bar;
}
在此塊內,識別符號可以按其宣告的方式使用。在此塊之外,必須新增namespace說明符(即,它必須是限定的)。例如,在namespace foo之外,bar必須寫成foo::bar。
C++ 包含另一個構造,它使這種冗長性變得不必要。透過將行using namespace foo;新增到程式碼段中,不再需要字首foo::。
沒有名稱的namespace稱為未命名名稱空間。對於這樣的namespace,將為每個翻譯單元生成一個唯一的名稱。無法將using關鍵字應用於未命名名稱空間,因此未命名名稱空間的工作方式就好像已將using關鍵字應用於它一樣。
- 語法
namespace {
declaration-list;
}
您可以為名稱空間建立新名稱(別名),包括巢狀名稱空間。
- 語法
namespace identifier = namespace-specifier;
- 使用
using namespace std;
這使用-指令指示程式中使用的但未宣告的任何名稱都應在“標準(std)”名稱空間中查詢。
要使來自名稱空間的單個名稱可用,以下使用-宣告存在
using foo::bar;
在此宣告之後,名稱bar可以在當前名稱空間內使用,而不是更冗長的版本foo::bar。請注意,程式設計師經常互換使用術語“宣告”和“指令”,儘管它們在技術上的含義不同。
最佳實踐是使用狹義的第二種形式(使用宣告),因為廣義的第一種形式(使用指令)可能會使比所需更多的名稱可用。示例
namespace foo {
int bar;
double pi;
}
using namespace foo;
int* pi;
pi = &bar; // ambiguity: pi or foo::pi?
在這種情況下,宣告using foo::bar;只會使foo::bar可用,避免了pi和foo::pi的衝突。此問題(相同名稱的變數或函式的衝突)稱為“名稱空間汙染”,原則上應儘可能避免。
使用-宣告可以出現在許多不同的位置。其中包括
- 名稱空間(包括預設名稱空間)
- 函式
一個使用-宣告使名稱(或名稱空間)在宣告的作用域內可用。示例
namespace foo {
namespace bar {
double pi;
}
using bar::pi;
// bar::pi can be abbreviated as pi
}
// here, pi is no longer an abbreviation. Instead, foo::bar::pi must be used.
名稱空間是分層的。在假設的名稱空間food::fruit中,識別符號orange引用food::fruit::orange(如果存在),否則引用food::orange(如果存在)。如果兩者都不存在,orange則引用預設名稱空間中的識別符號。
未在名稱空間中顯式宣告的程式碼被認為在預設名稱空間中。
名稱空間的另一個屬性是它們是開放的。一旦聲明瞭名稱空間,就可以重新宣告(重新開啟)它,並且可以新增名稱空間成員。示例
namespace foo {
int bar;
}
// ...
namespace foo {
double pi;
}
名稱空間最常用於避免命名衝突。儘管在最近的 C++ 程式碼中廣泛使用了名稱空間,但大多數舊程式碼未使用此功能。例如,整個標準庫是在名稱空間std中定義的,在該語言的早期標準中,在預設名稱空間中。
對於較長的名稱空間名稱,可以定義較短的別名(名稱空間別名宣告)。示例
namespace ultra_cool_library_for_image_processing_version_1_0 {
int foo;
}
namespace improc1 = ultra_cool_library_for_image_processing_version_1_0;
// from here, the above foo can be accessed as improc1::foo
存在一個特殊的名稱空間:未命名名稱空間。此名稱空間用於特定原始檔或其他名稱空間私有的名稱
namespace {
int some_private_variable;
}
// can use some_private_variable here
在周圍的作用域中,可以訪問未命名名稱空間的成員,而無需限定,即無需使用名稱空間名稱和::(因為名稱空間沒有名稱)。如果周圍的作用域是名稱空間,則可以將成員視為並訪問為其成員。但是,如果周圍的作用域是檔案,則無法從任何其他原始檔訪問成員,因為無法將檔案命名為作用域。未命名名稱空間宣告在語義上等效於以下構造
namespace $$$ {
// ...
}
using namespace $$$;
其中$$$是由編譯器生成的唯一識別符號。
您可以將未命名名稱空間巢狀在普通名稱空間中,反之亦然,您也可以巢狀兩個未命名的名稱空間。
namespace {
namespace {
// ok
}
}
由於空間限制,我們實際上無法顯示名稱空間命令的正確使用方法:它需要一個非常大的程式才能顯示其有效工作。但是,我們可以輕鬆地說明其概念本身。
// Namespaces Program, an example to illustrate the use of namespaces
#include <iostream>
namespace first {
int first1;
int x;
}
namespace second {
int second1;
int x;
}
namespace first {
int first2;
}
int main(){
//first1 = 1;
first::first1 = 1;
using namespace first;
first1 = 1;
x = 1;
second::x = 1;
using namespace second;
//x = 1;
first::x = 1;
second::x = 1;
first2 = 1;
//cout << 'X';
std::cout << 'X';
using namespace std;
cout << 'X';
return 0;
}
我們將從程式的開頭向下移動到程式的末尾檢查程式碼,依次檢查程式碼片段。
#include <iostream>
這僅包含iostream庫,以便我們可以使用std::cout將內容列印到螢幕上。
namespace first {
int first1;
int x;
}
namespace second {
int second1;
int x;
}
namespace first {
int first2;
}
我們建立一個名為first的名稱空間,並在其中新增兩個變數first1和x。然後我們關閉它。然後我們建立一個名為second的新名稱空間,並在其中放入兩個變數:second1和x。然後我們重新開啟名稱空間 first,並在其中新增另一個名為first2的變數。可以像這樣多次重新開啟名稱空間,以新增額外的名稱。
main(){
1 //first1 = 1;
2 first::first1 = 1;
主程式的第一行被註釋掉了,因為它會導致錯誤。為了訪問第一個名稱空間中的名稱,我們必須在變數名稱前加上其名稱空間的名稱和兩個冒號;因此,主程式的第二行不是語法錯誤。變數的名稱在作用域內:它只需要以這種特殊的方式被引用,然後才能在此處使用。因此,這將全域性名稱列表分割成組,每組都有自己的字首名稱。
3 using namespace first;
4 first1 = 1;
5 x = 1;
6 second::x = 1;
主程式的第三行引入了using namespace命令。此命令將第一個名稱空間中的所有名稱拉入作用域。然後可以從那裡以通常的方式使用它們。因此,程式的第四行和第五行可以編譯而不會出錯。特別是,變數x現在可用:為了訪問第二個名稱空間中的另一個變數x,我們將它稱為second::x,如第六行所示。因此,這兩個名為x的變數可以分別引用,就像它們在第五行和第六行一樣。
7 using namespace second;
8 //x = 1;
9 first::x = 1;
10 second::x = 1;
然後我們再次使用using namespace命令將名稱空間名為second中的宣告拉入。下一行被註釋掉了,因為它現在是一個錯誤(而之前是正確的)。由於這兩個名稱空間都已被引入全域性名稱列表,因此變數x現在變得不明確,並且需要僅以第九行和第十行所示的限定方式進行討論。
11 first2 = 1;
主程式的第十一行表明,即使first2是在名稱空間名為first的單獨部分中宣告的,它與名稱空間 first中的其他變數具有相同的狀態。可以根據需要多次重新開啟名稱空間。當然,作用域的通常規則適用:在同一個名稱空間中兩次宣告相同的名稱是非法的。
12 //cout << 'X';
13 std::cout << 'X';
14 using namespace std;
15 cout << 'X';
}
計算機中有一組特殊的檔案中定義了一個名稱空間。它的名稱是std,所有系統提供的名稱,例如cout,都在該名稱空間中的一系列不同檔案中宣告:這是一個非常大的名稱空間。請注意,程式最頂部的#include語句並沒有完全引入名稱空間:名稱在那裡,但仍然必須以限定形式引用。第十二行必須被註釋掉,因為目前系統提供的名稱(如cout)不可用,除非以限定形式std::cout,如第十三行所示。因此,我們需要像第十四行這樣的行:寫入該行後,所有系統提供的名稱都可用,如程式的最後一行所示。此時,我們已將三個名稱空間的名稱合併到程式中。
如示例程式所示,根據需要引入所需的宣告,並排除不需要的宣告,並且可以使用帶雙冒號的限定形式以受控的方式引入它們。這為大型程式提供了所需的更好的名稱控制。在上面的示例中,我們只使用了變數的名稱。但是,名稱空間也同樣根據需要控制過程和類的名稱。