C++編程中的命名空間基本知識講解
命名空間是一個聲明性區(qū)域,為其內(nèi)部的標識符(類型、函數(shù)和變量等的名稱)提供一個范圍。命名空間用于將代碼組織到邏輯組中,還可用于避免名稱沖突,尤其是在基本代碼包括多個庫時。命名空間范圍內(nèi)的所有標識符彼此可見,而沒有任何限制。命名空間之外的標識符可通過使用每個標識符的完全限定名(例如 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 指令,以將命名空間中的所有內(nèi)容引入范圍:
using namespace WidgetsUnlimited; ObjectManager mgr; mgr.DoSomething(); Func(mgr);
using 指令
通過 using 指令,可使用命名空間中的所有名稱,而不需要命名空間名稱為顯式限定符。如果在一個命名空間中使用多個不同的標識符,則在實現(xiàn)文件中使用 using 指令(即*.cpp);如果僅使用一個或兩個標識符,則考慮使用聲明,以僅將這些標識符而不是命名空間中的所有標識符引入范圍。如果本地變量的名稱與命名空間變量的名稱相同,則隱藏命名空間變量。使命名空間變量具有與全局變量相同的名稱是錯誤的。
注意
using 指令可以放置在 .cpp 文件的頂部(在文件范圍內(nèi)),或放置在類或函數(shù)定義內(nèi)。
一般情況下,避免將 using 指令放置在頭文件 (*.h) 中,因為任何包含該標頭的文件都會將命名空間中的所有內(nèi)容引入范圍,這將導致非常難以調(diào)試的名稱隱藏和名稱沖突問題。在頭文件中,始終使用完全限定名。如果這些名稱太長,可以使用命名空間別名將其縮短。(請參閱下文。)
聲明命名空間和命名空間成員
通常情況下,在頭文件中聲明一個命名空間。如果函數(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)此錯誤。
全局命名空間
如果未在顯式命名空間中聲明某個標識符,則該標識符屬于隱式全局命名空間的一部分。通常情況下,如果可能,嘗試避免在全局范圍內(nèi)進行聲明,入口點 main 函數(shù)除外,它必須位于全局命名空間中。若要顯式限定全局標識符,請使用沒有名稱的范圍解析運算符,如 ::SomeFunction(x); 中所示。這將使標識符與任何其他命名空間中具有相同名稱的任何內(nèi)容區(qū)分開來,并且還有助于使其他人更輕松地了解你的代碼。
Std 命名空間
所有 C++ 標準庫類型和函數(shù)都在 std 命名空間或嵌套在 std 內(nèi)的命名空間中進行聲明。
嵌套命名空間
可以嵌套命名空間。普通的嵌套命名空間具有對其父級成員的非限定訪問權限,而父成員不具有對嵌套命名空間的非限定訪問權限(除非它被聲明為內(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)部實現(xiàn)詳細信息。
內(nèi)聯(lián)命名空間 (C++ 11)
與普通嵌套命名空間不同,內(nèi)聯(lián)命名空間的成員會被視為父命名空間的成員。這一特性使針對重載函數(shù)的依賴于參數(shù)的查找可以對父命名空間和嵌套內(nèi)聯(lián)命名空間中具有重載的函數(shù)起作用。它還可讓你在內(nèi)聯(liá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)命名空間中聲明的模板的父命名空間中聲明專用化:
namespace Parent
{
inline namespace new_ns
{
template <typename T>
struct C
{
T member;
};
}
template<>
class C<int> {};
}
可以將內(nèi)聯(lián)命名空間用作版本控制機制,以管理對庫的公共接口的更改。例如,可以創(chuàng)建單個父命名空間,并將接口的每個版本封裝到嵌套在父命名空間內(nèi)的其自己的命名空間中。保留最新或首選的版本的命名空間限定為內(nèi)聯(lián),并因此以父命名空間的直接成員的形式公開。調(diào)用 Parent::Class 的客戶端代碼將自動綁定到新代碼。通過使用指向包含該代碼的嵌套命名空間的完全限定路徑,選擇使用較舊版本的客戶端仍可以對其進行訪問。
Inline 關鍵字必須應用到編譯單元中命名空間的第一個聲明中。
下面的示例演示一個接口的兩個版本,每個版本位于一個嵌套命名空間中。通過 v_10 接口對 v_20 命名空間進行了某些修改,且該命名空間被標記為內(nèi)聯(lián)。使用新庫并調(diào)用 Contoso::Funcs::Add 的客戶端代碼將調(diào)用 v_20 版本。嘗試調(diào)用 Contoso::Funcs::Divide 的代碼現(xiàn)在將獲取一個編譯時錯誤。如果它們確實需要該函數(shù),則仍可以通過顯式調(diào)用 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 指令的頭文件中進行鍵入單調(diào)乏味,則可以使用用作實際名稱的縮寫的命名空間別名。例如:
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(){}
}
這稱為未命名的命名空間或匿名命名空間,在你想要使變量聲明對于其他文件中的代碼不可見(即為它們提供內(nèi)部鏈接),而不必創(chuàng)建已命名的命名空間時非常有用。同一文件中的所有代碼都可以看到未命名的命名空間中的標識符,但這些標識符以及命名空間本身在該文件外部(或更準確地說,在翻譯單元外部)不可見。
上一篇:詳解在C++中顯式默認設置的函數(shù)和已刪除的函數(shù)的方法
欄 目:C語言
下一篇:Mac OS上搭建Apache+PHP+MySQL開發(fā)環(huán)境的詳細教程
本文標題:C++編程中的命名空間基本知識講解
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2548.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10深入理解C++中常見的關鍵字含義
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10c++中inline的用法分析
- 01-10如何尋找數(shù)組中的第二大數(shù)
- 01-10用C++實現(xiàn)DBSCAN聚類算法
- 01-10全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關鍵字的使用詳解


閱讀排行
本欄相關
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實例總結
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 01-10使用C語言求解撲克牌的順子及n個骰子


