1. 程式人生 > >C++ 名稱空間namespace【轉】

C++ 名稱空間namespace【轉】

(轉自:https://blog.csdn.net/touzani/article/details/1637776?utm_source=blogxgwz7

名稱空間

在C++中,名稱(name)可以是符號常量、變數、巨集、函式、結構、列舉、類和物件等等。為了避免,在大規模程式的設計中,以及在程式設計師使用各種各樣的C++庫時,這些識別符號的命名發生衝突,標準C++引入了關鍵字namespace(名稱空間/名字空間/名稱空間/名域),可以更好地控制識別符號的作用域。

MFC中並沒有使用名稱空間,但是在.NET框架、MC++和C++/CLI中,都大量使用了名稱空間。

1)作用域與名稱空間

相關概念


與名稱空間相關的概念有:

  • 宣告域(declaration region)—— 宣告識別符號的區域。如在函式外面宣告的全域性變數,它的宣告域為宣告所在的檔案。在函式內宣告的區域性變數,它的宣告域為宣告所在的程式碼塊(例如整個函式體或整個複合語句)。
  • 潛在作用域(potential scope)—— 從宣告點開始,到宣告域的末尾的區域。因為C++採用的是先聲明後使用的原則,所以在宣告點之前的宣告域中,識別符號是不能用的。即,識別符號的潛在作用域,一般會小於其宣告域。
  • 作用域(scope)—— 識別符號對程式可見的範圍。識別符號在其潛在作用域內,並非在任何地方都是可見的。例如,區域性變數可以遮蔽全域性變數、巢狀層次中的內層變數可以遮蔽外層變數,從而被遮蔽的全域性或外層變數在其倍遮蔽的區域內是不可見的。所以,一個識別符號的作用域可能小於其潛在作用域。


 

名稱空間


名稱空間(namespace)是一種描述邏輯分組的機制,可以將按某些標準在邏輯上屬於同一個集團的宣告放在同一個名稱空間中。

原來C++識別符號的作用域分成三級:程式碼塊({……},如複合語句和函式體)、類和全域性。現在,在其中的類和全域性之間,標準C++又添加了名稱空間這一個作用域級別。

名稱空間可以是全域性的,也可以位於另一個名稱空間之中,但是不能位於類和程式碼塊中。所以,在名稱空間中宣告的名稱(識別符號),預設具有外部連結特性(除非它引用了常量)。

在所有名稱空間之外,還存在一個全域性名稱空間,它對應於檔案級的宣告域。因此,在名稱空間機制中,原來的全域性變數,現在被認為位於全域性名稱空間中。

標準C++庫(不包括標準C庫)中所包含的所有內容(包括常量、變數、結構、類和函式等)都被定義在名稱空間std(standard標準)中了。

2)定義名稱空間

有兩種形式的名稱空間——有名的和無名的。

名稱空間的定義格式為:(取自C++標準文件)

named-namespace-definition:

       namespace identifier { namespace-body }

unnamed-namespace-definition:

       namespace { namespace-body }

namespace-body:

       declaration-seqopt

即:(自己翻譯並改寫的)

有名的名稱空間:

       namespace 名稱空間名 {

              宣告序列可選

       }
無名的名稱空間:

       namespace {

              宣告序列可選

       }

名稱空間的成員,是在名稱空間定義中的花括號內聲明瞭的名稱。可以在名稱空間的定義內,定義名稱空間的成員(內部定義)。也可以只在名稱空間的定義內宣告成員,而在名稱空間的定義之外,定義名稱空間的成員(外部定義)。

 

名稱空間成員的外部定義的格式為:

名稱空間名::成員名 ……

例如:

// out.h
namespace Outer { // 名稱空間Outer的定義

       int i; // 名稱空間Outer的成員i的內部定義

       namespace Inner { // 子名稱空間Inner的內部定義
              void f() { i++; } // 名稱空間Inner的成員f()的內部定義,其中的i為Outer::i
              int i;
              void g() { i++; } // 名稱空間Inner的成員g()的內部定義,其中的i為Inner::i
              void h(); // 名稱空間Inner的成員h()的宣告
       }

       void f(); // 名稱空間Outer的成員f()的宣告
       // namespace Inner2; // 錯誤,不能宣告子名稱空間
}

void Outer::f() {i--;} // 名稱空間Outer的成員f()的外部定義
void Outer::Inner::h() {i--;} // 名稱空間Inner的成員h()的外部定義
// namespace Outer::Inner2 {/*……*/} // 錯誤,不能在外部定義子名稱空間

 

注意:

  • 不能在名稱空間的定義中宣告(另一個巢狀的)子名稱空間,只能在名稱空間的定義中定義子名稱空間。
  • 也不能直接使用“名稱空間名::成員名 ……”定義方式,為名稱空間新增新成員,而必須先在名稱空間的定義中新增新成員的宣告。
  • 另外,名稱空間是開放的,即可以隨時把新的成員名稱加入到已有的名稱空間之中去。方法是,多次宣告和定義同一名稱空間,每次新增自己的新成員和名稱。例如:
namespace A {

       int i;

       void f();

} // 現在A有成員i和f()

namespace A {

       int j;

       void g();

} // 現在A有成員i、f()、j和g()

還可以用多種方法,來組合現有的名稱空間,讓它們為我所用。例如:

namespace My_lib {

       using namespace His_string;

       using namespace Her_vector;

       using Your_list::List;

       void my_f(String &, List &);

}

……

using namespace My_lib;

……

Vector<String> vs[5];

List<int> li[10];

my_f(vs[2], li[5]);


 

3)使用名稱空間

作用域解析運算子(::)


對名稱空間中成員的引用,需要使用名稱空間的作用域解析運算子::。例如:

// out1.cpp
#include "out.h"
#include <iostream>

int main ( ) {

       Outer::i = 0;

       Outer::f(); // Outer::i = -1;

       Outer::Inner::f(); // Outer::i = 0;

       Outer::Inner::i = 0;

       Outer::Inner::g(); // Inner::i = 1;

       Outer::Inner::h(); // Inner::i = 0;

       std::cout << "Hello, World!" << std::endl;

       std::cout << "Outer::i = " << Outer::i << ",  Inner::i = " << Outer::Inner::i << std::endl;

}

 

using指令(using namespace)


為了省去每次呼叫Inner成員和標準庫的函式和物件時,都要新增Outer::Inner::和sta::的麻煩,可以使用標準C++的using編譯指令來簡化對名稱空間中的名稱的使用。格式為:

using namespace 名稱空間名[::名稱空間名……];

在這條語句之後,就可以直接使用該名稱空間中的識別符號,而不必寫前面的名稱空間定位部分。因為using指令,使所指定的整個名稱空間中的所有成員都直接可用。例如:

// out2.cpp

#include "out.h"
#include <iostream>

// using namespace Outer; // 編譯錯誤,因為變數i和函式f()有名稱衝突
using namespace Outer::Inner;
using namespace std;

int main ( ) {
       Outer::i = 0;

       Outer::f(); // Outer::i = -1;

       f(); // Inner::f(),Outer::i = 0;

       i = 0; // Inner::i

       g(); // Inner::g(),Inner::i = 1;

       h(); // Inner::h(),Inner::i = 0;

       cout << "Hello, World!" << endl;

       cout << "Outer::i = " << Outer::i << ",  Inner::i = " << i << endl;
}

又例如:(.NET框架)

using namespace System::Drawing::Imaging;

using namespace System::Window::Forms::Design::Behavior;


 

using宣告(using)


除了可以使用using編譯指令(組合關鍵字using namespace)外,還可以使用using宣告來簡化對名稱空間中的名稱的使用。格式為:

using 名稱空間名::[名稱空間名::……]成員名;

注意,關鍵字using後面並沒有跟關鍵字namespace,而且最後必須為名稱空間的成員名(而在using編譯指令的最後,必須為名稱空間名)。

與using指令不同的是,using宣告只是把名稱空間的特定成員的名稱,新增該宣告所在的區域中,使得該成員可以不需要採用,(多級)名稱空間的作用域解析運算子來定位,而直接被使用。但是該名稱空間的其他成員,仍然需要作用域解析運算子來定位。例如:

// out3.cpp

#include "out.h"
#include <iostream>

using namespace Outer; // 注意,此處無::Inner
using namespace std;

// using Inner::f; // 編譯錯誤,因為函式f()有名稱衝突
using Inner::g; // 此處省去Outer::,是因為Outer已經被前面的using指令作用過了
using Inner::h;

int main ( ) {
       i = 0; // Outer::i

       f(); // Outer::f(),Outer::i = -1;

       Inner::f(); // Outer::i = 0;

       Inner::i = 0;

       g(); // Inner::g(),Inner::i = 1;

       h(); // Inner::h(),Inner::i = 0;

       cout << "Hello, World!" << endl;

       cout << "Outer::i = " << i << ",  Inner::i = " << Inner::i << endl;
}

 

using指令與using宣告的比較

 

可見,using編譯指令和using宣告,都可以簡化對名稱空間中名稱的訪問。

using指令使用後,可以一勞永逸,對整個名稱空間的所有成員都有效,非常方便。而using宣告,則必須對名稱空間的不同成員名稱,一個一個地去宣告,非常麻煩。

但是,一般來說,使用using宣告會更安全。因為,using宣告只匯入指定的名稱,如果該名稱與區域性名稱發生衝突,編譯器會報錯。而using指令匯入整個名稱空間中的所有成員的名稱,包括那些可能根本用不到的名稱,如果其中有名稱與區域性名稱發生衝突,則編譯器並不會發出任何警告資訊,而只是用區域性名去自動覆蓋名稱空間中的同名成員。特別是名稱空間的開放性,使得一個名稱空間的成員,可能分散在多個地方,程式設計師難以準確知道,別人到底為該名稱空間添加了哪些名稱。

雖然使用名稱空間的方法,有多種可供選擇。但是不能貪圖方便,一味使用using 指令,這樣就完全背離了設計名稱空間的初衷,也失去了名稱空間應該具有的防止名稱衝突的功能。

一般情況下,對偶爾使用的名稱空間成員,應該使用名稱空間的作用域解析運算子來直接給名稱定位。而對一個大名稱空間中的經常要使用的少數幾個成員,提倡使用using宣告,而不應該使用using編譯指令。只有需要反覆使用同一個名稱空間的許多數成員時,使用using編譯指令,才被認為是可取的。

例如,如果一個程式(如上面的outi.cpp)只使用一兩次cout,而且也不使用std名稱空間中的其他成員,則可以使用名稱空間的作用域解析運算子來直接定位。如:

#include <iostream>

……

std::cout << "Hello, World!" << std::endl;

std::cout << "Outer::i = " << Outer::i << ",  Inner::i = " << Outer::Inner::i << std::endl;

又例如,如果一個程式要反覆使用std名稱空間中的cin、cout和cerr(如上面的outi.cpp),而不怎麼使用其他std名稱空間中的其他成員,則應該使用using 宣告而不是using指令。如:

#include <iostream>

……

using std::cout;

cout << "Hello, World!" << endl;

cout << "Outer::i = " << Outer::i << ",  Inner::i = " << Outer::Inner::i << endl;

4)名稱空間的名稱

命名空間別名


標準C++引入名稱空間,主要是為了避免成員的名稱衝突。若果使用者都給自己的名稱空間取簡短的名稱,那麼這些(往往同是全域性級的)名稱空間本身,也可能發生名稱衝突。如果為了避免衝突,而為名稱空間取很長的名稱,則使用起來就會不方便。這是一個典型的兩難問題。

標準C++為此提供了一種解決方案——名稱空間別名,格式為:

namespace 別名 = 名稱空間名;

例如:(AT&T美國電話電報公司)

namespace American_Telephone_and_Telegraph { // 名稱空間名太長

       class String {
              String(const char*);
              // ……
       }
}

American_Telephone_and_Telegraph::String s1 // 使用不方便
    = new American_Telephone_and_Telegraph::String("Grieg");

namespace ATT = American_Telephone_and_Telegraph; // 定義別名

ATT::String s2 = new ATT::String("Bush"); // 使用方便

ATT::String s3 = new ATT::String("Nielsen");

 

無名名稱空間


標準C++引入名稱空間,除了可以避免成員的名稱發生衝突之外,還可以使程式碼保持區域性性,從而保護程式碼不被他人非法使用。如果你的目的主要是後者,而且又為替名稱空間取一個好聽、有意義、且與別人的名稱空間不重名的名稱而煩惱的話,標準C++還允許你定義一個無名名稱空間。你可以在當前編譯單元中(無名名稱空間之外),直接使用無名名稱空間中的成員名稱,但是在當前編譯單元之外,它又是不可見的。

無名名稱空間的定義格式為:

namespace {
       宣告序列可選
}

實際上,上面的定義等價於:(標準C++中有一個隱含的使用指令)

namespace $$$ {
       宣告序列可選
}

using namespace $$$;

例如:

namespace {
       int i;
       void f() {/*……*/}
}

int main() {
       i = 0; // 可直接使用無名名稱空間中的成員i
       f(); // 可直接使用無名名稱空間中的成員f()
}


--------------------- 
作者:touzani 
來源:CSDN 
原文:https://blog.csdn.net/touzani/article/details/1637776?utm_source=copy 
版權宣告:本文為博主原創文章,轉載請附上博文連結!