C++編程中的命名空間基本知識(shí)講解
命名空間是一個(gè)聲明性區(qū)域,為其內(nèi)部的標(biāo)識(shí)符(類(lèi)型、函數(shù)和變量等的名稱(chēng))提供一個(gè)范圍。命名空間用于將代碼組織到邏輯組中,還可用于避免名稱(chēng)沖突,尤其是在基本代碼包括多個(gè)庫(kù)時(shí)。命名空間范圍內(nèi)的所有標(biāo)識(shí)符彼此可見(jiàn),而沒(méi)有任何限制。命名空間之外的標(biāo)識(shí)符可通過(guò)使用每個(gè)標(biāo)識(shí)符的完全限定名(例如 std::vector<std::string> vec;)來(lái)訪問(wèn)成員,也可通過(guò)單個(gè)標(biāo)識(shí)符的 using 聲明 (using std::string) 或命名空間中所有標(biāo)識(shí)符的 using 指令 (C++) (using namespace std;) 來(lái)訪問(wèn)成員。頭文件中的代碼應(yīng)始終使用完全限定的命名空間名稱(chēng)。
下面的示例演示了一個(gè)命名空間聲明和命名空間之外的代碼可訪問(wèn)其成員的三種方法。
namespace ContosoData { class ObjectManager { public: void DoSomething() {} }; void Func(ObjectManager) {} }
使用完全限定名:
ContosoData::ObjectManager mgr; mgr.DoSomething(); ContosoData::Func(mgr);
使用 using 聲明,以將一個(gè)標(biāo)識(shí)符引入范圍:
using WidgetsUnlimited::ObjectManager; ObjectManager mgr; mgr.DoSomething();
使用 using 指令,以將命名空間中的所有內(nèi)容引入范圍:
using namespace WidgetsUnlimited; ObjectManager mgr; mgr.DoSomething(); Func(mgr);
using 指令
通過(guò) using 指令,可使用命名空間中的所有名稱(chēng),而不需要命名空間名稱(chēng)為顯式限定符。如果在一個(gè)命名空間中使用多個(gè)不同的標(biāo)識(shí)符,則在實(shí)現(xiàn)文件中使用 using 指令(即*.cpp);如果僅使用一個(gè)或兩個(gè)標(biāo)識(shí)符,則考慮使用聲明,以?xún)H將這些標(biāo)識(shí)符而不是命名空間中的所有標(biāo)識(shí)符引入范圍。如果本地變量的名稱(chēng)與命名空間變量的名稱(chēng)相同,則隱藏命名空間變量。使命名空間變量具有與全局變量相同的名稱(chēng)是錯(cuò)誤的。
注意
using 指令可以放置在 .cpp 文件的頂部(在文件范圍內(nèi)),或放置在類(lèi)或函數(shù)定義內(nèi)。
一般情況下,避免將 using 指令放置在頭文件 (*.h) 中,因?yàn)槿魏伟摌?biāo)頭的文件都會(huì)將命名空間中的所有內(nèi)容引入范圍,這將導(dǎo)致非常難以調(diào)試的名稱(chēng)隱藏和名稱(chēng)沖突問(wèn)題。在頭文件中,始終使用完全限定名。如果這些名稱(chēng)太長(zhǎng),可以使用命名空間別名將其縮短。(請(qǐng)參閱下文。)
聲明命名空間和命名空間成員
通常情況下,在頭文件中聲明一個(gè)命名空間。如果函數(shù)實(shí)現(xiàn)位于一個(gè)單獨(dú)的文件中,則限定函數(shù)名稱(chēng),如本示例所示。
//contosoData.h #pragma once namespace ContosoDataServer { void Foo(); int Bar(); } contosodata.cpp 中的函數(shù)實(shí)現(xiàn)應(yīng)使用完全限定名,即使將一個(gè) using 指令放置于文件的頂部也是如此: #include "contosodata.h" using namespace ContosoDataServer; void ContosoDataServer::Foo() { //no qualification because using directive above Bar(); } int ContosoDataServer::Bar(){return 0;}
可以在單個(gè)文件中的多個(gè)塊中聲明命名空間,也可在多個(gè)文件中聲明命名空間。編譯器在預(yù)處理過(guò)程中將各部分聯(lián)接在一起,產(chǎn)生的命名空間中包含所有部分中聲明的所有成員。一個(gè)相關(guān)示例是在標(biāo)準(zhǔn)庫(kù)中的每個(gè)頭文件中聲明的 std 命名空間。
指定的命名空間的成員可以在定義的名稱(chēng)的顯式限定所聲明的命名空間的外部進(jìn)行定義。但是,定義必須出現(xiàn)在命名空間中的聲明位置之后,該命名空間包含在聲明的命名空間中。例如:
// defining_namespace_members.cpp // C2039 expected namespace V { void f(); } void V::f() { } // ok void V::g() { } // C2039, g() is not yet a member of V namespace V { void g(); } }
當(dāng)跨多個(gè)頭文件聲明命名空間成員,并且未以正確的順序包含這些標(biāo)頭時(shí),可能出現(xiàn)此錯(cuò)誤。
全局命名空間
如果未在顯式命名空間中聲明某個(gè)標(biāo)識(shí)符,則該標(biāo)識(shí)符屬于隱式全局命名空間的一部分。通常情況下,如果可能,嘗試避免在全局范圍內(nèi)進(jìn)行聲明,入口點(diǎn) main 函數(shù)除外,它必須位于全局命名空間中。若要顯式限定全局標(biāo)識(shí)符,請(qǐng)使用沒(méi)有名稱(chēng)的范圍解析運(yùn)算符,如 ::SomeFunction(x); 中所示。這將使標(biāo)識(shí)符與任何其他命名空間中具有相同名稱(chēng)的任何內(nèi)容區(qū)分開(kāi)來(lái),并且還有助于使其他人更輕松地了解你的代碼。
Std 命名空間
所有 C++ 標(biāo)準(zhǔn)庫(kù)類(lèi)型和函數(shù)都在 std 命名空間或嵌套在 std 內(nèi)的命名空間中進(jìn)行聲明。
嵌套命名空間
可以嵌套命名空間。普通的嵌套命名空間具有對(duì)其父級(jí)成員的非限定訪問(wèn)權(quán)限,而父成員不具有對(duì)嵌套命名空間的非限定訪問(wèn)權(quán)限(除非它被聲明為內(nèi)聯(lián)),如下面的示例所示:
namespace ContosoDataServer { void Foo(); namespace Details { int CountImpl; void Ban() { return Foo(); } } int Bar(){...}; int Baz(int i) { return Details::CountImpl; } }
普通嵌套命名空間可用于封裝不屬于父命名空間的公共接口的一部分的內(nèi)部實(shí)現(xiàn)詳細(xì)信息。
內(nèi)聯(lián)命名空間 (C++ 11)
與普通嵌套命名空間不同,內(nèi)聯(lián)命名空間的成員會(huì)被視為父命名空間的成員。這一特性使針對(duì)重載函數(shù)的依賴(lài)于參數(shù)的查找可以對(duì)父命名空間和嵌套內(nèi)聯(lián)命名空間中具有重載的函數(shù)起作用。它還可讓你在內(nèi)聯(lián)命名空間中聲明的模板的父命名空間中聲明專(zhuān)用化。下面的示例演示在默認(rèn)情況下,外部代碼如何綁定到內(nèi)聯(lián)命名空間:
//Header.h #include <string> namespace Test { namespace old_ns { std::string Func() { return std::string("Hello from old"); } } inline namespace new_ns { std::string Func() { return std::string("Hello from new"); } } } #include "header.h" #include <string> #include <iostream> int main() { using namespace Test; using namespace std; string s = Func(); std::cout << s << std::endl; // "Hello from new" return 0; }
下面的示例演示如何在內(nèi)聯(lián)命名空間中聲明的模板的父命名空間中聲明專(zhuān)用化:
namespace Parent { inline namespace new_ns { template <typename T> struct C { T member; }; } template<> class C<int> {}; }
可以將內(nèi)聯(lián)命名空間用作版本控制機(jī)制,以管理對(duì)庫(kù)的公共接口的更改。例如,可以創(chuàng)建單個(gè)父命名空間,并將接口的每個(gè)版本封裝到嵌套在父命名空間內(nèi)的其自己的命名空間中。保留最新或首選的版本的命名空間限定為內(nèi)聯(lián),并因此以父命名空間的直接成員的形式公開(kāi)。調(diào)用 Parent::Class 的客戶(hù)端代碼將自動(dòng)綁定到新代碼。通過(guò)使用指向包含該代碼的嵌套命名空間的完全限定路徑,選擇使用較舊版本的客戶(hù)端仍可以對(duì)其進(jìn)行訪問(wèn)。
Inline 關(guān)鍵字必須應(yīng)用到編譯單元中命名空間的第一個(gè)聲明中。
下面的示例演示一個(gè)接口的兩個(gè)版本,每個(gè)版本位于一個(gè)嵌套命名空間中。通過(guò) v_10 接口對(duì) v_20 命名空間進(jìn)行了某些修改,且該命名空間被標(biāo)記為內(nèi)聯(lián)。使用新庫(kù)并調(diào)用 Contoso::Funcs::Add 的客戶(hù)端代碼將調(diào)用 v_20 版本。嘗試調(diào)用 Contoso::Funcs::Divide 的代碼現(xiàn)在將獲取一個(gè)編譯時(shí)錯(cuò)誤。如果它們確實(shí)需要該函數(shù),則仍可以通過(guò)顯式調(diào)用 Contoso::v_10::Funcs::Divide 訪問(wèn) v_10 版本。
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); }; } }
命名空間別名
命名空間名稱(chēng)必須是唯一的,這意味著通常它們不應(yīng)太短。如果名稱(chēng)的長(zhǎng)度使代碼難以閱讀,或在不能使用 using 指令的頭文件中進(jìn)行鍵入單調(diào)乏味,則可以使用用作實(shí)際名稱(chēng)的縮寫(xiě)的命名空間別名。例如:
namespace a_very_long_namespace_name { class Foo {}; } namespace AVLNN = a_very_long_namespace_name; void Bar(AVLNN::Foo foo){ }
匿名或未命名的命名空間
可以創(chuàng)建顯式命名空間,但不為其提供一個(gè)名稱(chēng):
namespace { int MyFunc(){} }
這稱(chēng)為未命名的命名空間或匿名命名空間,在你想要使變量聲明對(duì)于其他文件中的代碼不可見(jiàn)(即為它們提供內(nèi)部鏈接),而不必創(chuàng)建已命名的命名空間時(shí)非常有用。同一文件中的所有代碼都可以看到未命名的命名空間中的標(biāo)識(shí)符,但這些標(biāo)識(shí)符以及命名空間本身在該文件外部(或更準(zhǔn)確地說(shuō),在翻譯單元外部)不可見(jiàn)。
相關(guān)文章
詳解C/C++中低耦合代碼的設(shè)計(jì)實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了C/C++?相關(guān)低耦合代碼的設(shè)計(jì)實(shí)現(xiàn),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++有一定的幫助,感興趣的小伙伴可以了解一下2023-01-01C語(yǔ)言調(diào)試手段:鎖定錯(cuò)誤的實(shí)現(xiàn)方法
本篇文章是對(duì)在C語(yǔ)言調(diào)試中,鎖定錯(cuò)誤的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++ stack與queue模擬實(shí)現(xiàn)詳解
這篇文章主要給大家介紹了關(guān)于c++stack與queue模擬實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08C語(yǔ)言多種方法實(shí)現(xiàn)一個(gè)函數(shù)左旋字符串中K個(gè)字符
這篇文章主要為大家介紹了C語(yǔ)言多種方法實(shí)現(xiàn)一個(gè)函數(shù),可以左旋字符串中K個(gè)字符,文中附含詳細(xì)的示例講解,有需要的朋友可以借鑒參考下2021-10-10C++友元(Friend)用法實(shí)例簡(jiǎn)介
這篇文章主要介紹了C++友元(Friend)用法,對(duì)于C++的學(xué)習(xí)來(lái)說(shuō)有很好的參考價(jià)值,需要的朋友可以參考下2014-08-08C語(yǔ)言 數(shù)據(jù)結(jié)構(gòu)中棧的實(shí)現(xiàn)代碼
這篇文章主要介紹了C語(yǔ)言 數(shù)據(jù)結(jié)構(gòu)中棧的實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10