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