420 likes | 525 Views
Chap 16 名稱空間. 子曰:「名不正,則言不順 ﹔ 言不順,則事不成。」 《 論語 ﹒ 子路第十三 》 名稱空間 (namespace) 的目的在於避免大型程式庫之間因為使用相同名稱而造成衝突。. Chap 16 名稱空間. 16.1 因為名稱相同而造成的問題 16.2 名稱空間的基本語法 16.3 名稱空間成員的存取 16.4 使用「 using 指令」和「 using 宣 告」以存取名稱空間的成員 16.5 標準名稱空間 16.6 未命名的名稱空間. 因為名稱相同而造成的問題.
E N D
Chap 16 名稱空間 子曰:「名不正,則言不順﹔言不順,則事不成。」 《論語﹒子路第十三》 名稱空間 (namespace)的目的在於避免大型程式庫之間因為使用相同名稱而造成衝突。
Chap 16 名稱空間 • 16.1 因為名稱相同而造成的問題 • 16.2 名稱空間的基本語法 • 16.3 名稱空間成員的存取 • 16.4 使用「using指令」和「using宣 告」以存取名稱空間的成員 • 16.5 標準名稱空間 • 16.6 未命名的名稱空間
因為名稱相同而造成的問題 • 開發大型程式時,因為名稱相同而造成問題的機會就會大增。 • 限制了我們使用同名常數和同名類別的機會,否則在編譯時就會發生重複宣告(multiple declaration) 的錯誤,無法正確使用程式庫 (library)。
因為名稱衝突而造成無法引用既有程式的問題 • 由於常數Gain和struct資料型態Member的名稱相同,一次只能選擇一個標頭檔使用
名稱空間的基本語法 • 名稱空間 (name space) 的宣告敘述之後沒有「;」做為結尾。例如: namespace MyNamespace // 程式庫的介面 { const double Gain = 5.9; // 常數的定義 struct Member // struct資料型態的宣告 { char Name[20]; int Phone; }; double Fnc(double); // 函數的宣告(函數的原型) }
名稱空間的宣告可以用累積的方式完成 namespace MyNamespace { const double Gain = 5.9; } namespace MyNamespace { struct Member { char Name[20]; int Phone; }; double Fnc(double); }
名稱空間成員的存取 • 名稱空間的成員(member): 名稱空間所含的各個項目。 • 「::」: 範圍確認運算子(scope resolution operator) 。 • 要存取名稱空間的成員時:名稱空間的名稱 + 範圍確認運算子 + 該成員的名稱。 • 例如: MyNamespace::Gain; MyNamespace::Fnc();
資格修飾子(qualifier) • 資格修飾子: 名稱空間的名稱加上範圍確認運算子。「MyNamespace::」就是一個典型的資格修飾子。 • 使用名稱空間中宣告的struct資料型態來定義實例(instance) : MyNamespace::Member Ma = {"George Wang", 23246352}; • 也就是說,耍在struct資料型態的名稱前加上資格修飾子。
巢狀名稱空間 (nested namespace) • 名稱空間的成員是另一個名稱空間。例如: namespace NS1 { int Size; namespace NS2 { const double Ratio; } } • 巢狀名稱空間的內部成員必需套用所有名稱空間的名稱: int x = NS1::Size; double y = NS1::NS2::Ratio;
名稱空間的別名 (alias) • 簡化超長的名稱空間名稱。例如,有個名稱很長的名稱空間: namespace Micro_Soft-Company_Namespace { char Employer[2500][20]; double Fnc(double); } • 可以使用名稱空間別名敘述 (namespace alias statement ): namespace MSC = Micro_Soft-Company_Namespace;
使用名稱空間別名 (alias) 存取成員 • 可以使用較簡短的方式存取其成員,例如: char Head[20] = MSC::Employer[23]; • 使用名稱空間別名敘述可以一次取代兩層巢狀名稱空間的關係: namespace NS12 = NS1::NS2; • 此後成員的存取就可以精簡成: double y = NS12::Ratio;
程式庫的分割: 介面與實作 • 介面部份(interface part): 包括常數宣告和函數原型的部份。 • 實作部份(implementation part): 含有演算細節之函數定義的部份。 • 通常進一步將龐大的程式庫區分成數個檔案。譬如說,可以將「介面部份」儲存成一個叫做MyLib.h的檔案,而將「實作部份」儲存成一個稱為MyLib.cpp的檔案。
實作部份的寫法 (1/2) • 在名稱空間主體之中定義函數例如: // MyLib.cpp #include "MyLib.h" namespace MyNamespace { double Fnc(double x) { // 函數 Fnc() 的演算細節 } }
實作部份的寫法 (2/2) • 在名稱空間主體之外定義函數例如: // MyLib.cpp #include "MyLib.h" double MyNamespace::Fnc(double x) { // 函數Fnc()的演算細節 }
實例介紹 • 在檔案First.h和Second.h內,分別宣告了NS1和NS2兩個名稱空間。NS1和NS2兩個名稱空間的同名函數Fnc() 實作部份寫在檔案 FncLib.cpp裏面。 • 檔案Test.cpp是主程式,分別使用名稱空間NS1和NS2中的各成員。 • 以Borland C++ Command Line Compiler的使用方式為例,本程式在DOS下的編譯指令為: bcc32 Test FncLib
範例程式 檔案 First.h // First.h #ifndef FIRST_H #define FIRST_H namespaceNS1 // 宣告名稱空間 NS1 { const double Gain = 5.9; struct Member { char Name[20]; int Phone; }; double Fnc(double); } #endif
範例程式 檔案 Second.h // Second.h #ifndef SECOND_H #define SECOND_H namespaceNS2 // 宣告名稱空間 NS2 { const double Gain = 2.6; struct Member { int IdNum; char Name[20]; double Profit; }; double Fnc(double); } #endif
範例程式 檔案 FncLib.cpp // FncLib.cpp #include "First.h" // 引入NS1的宣告 #include "Second.h"// 引入NS2的宣告 // 定義函數 NS1::Fnc() double NS1::Fnc(double x) {return x * NS1::Gain;} // 定義函數 NS2::Fnc() double NS2::Fnc(double x) {return x * NS2::Gain;}
範例程式 檔案 Array2Fnc.cpp // TestHead.cpp #include <iostream> using namespace std; #include "First.h" // 引入NS1的宣告 #include "Second.h"// 引入NS2的宣告 // ----主程式---------------------- int main() { NS1::Member Ma={"George Wang", 23246352}; NS2::Member Mb={34,"Peter White", 12.67};
cout << "Name of Ma is: " << Ma.Name << endl; cout << "Name of Mb is: " << Mb.Name << endl;; cout << "NS1::Gain is: " << NS1::Gain << endl; cout << "NS2::Gain is: " << NS2::Gain << endl; cout << "NS1::Fnc(2.5) is: " << NS1::Fnc(2.5) << endl; cout << "NS2::Fnc(2.5) is: " << NS2::Fnc(2.5) << endl; return 0; }
使用「using指令」以存取名稱空間的成員 • 為了避免在存取時使用諸如 double x = MyNamespace::Ratio; 的冗長成員名稱,可以在程式中加入「using 指令」 (using directive) 加以簡化。例如: using namespace MyNamespace; … double x = Ratio;
using指令 • 如果曾經在程式中宣告了名稱空間MyNamespace,則上述using指令相當於在指令的地方加入了下列四個宣告敘述: const double Gain = 5.9; struct Member { char Name[20]; int Phone; }; double Fnc(double);
名稱空間成員的適用範圍 • 局部變數的優先權高於全域變數 (global variables) 和名稱空間成員。 • 如果using 指令或using 宣告寫在區塊之內,則其作用範圍就只有到那個區塊結尾的地方。
範例程式 檔案 Priority.cpp // Priority.cpp #include <iostream> using namespace std; namespaceNS// 宣告名稱空間 NS { int M = 10; // (1) float x =1.0, y = 2.0; // (2) char C = 'G'; // (3) } // ----主程式----------------------- int main() { NS::M = 6; // 更動(1)中的M
{ float y = 7.5; // (4)定義局部變數 y using namespaceNS;// using 指令 x = 5.6; // 更動(2) 中的 x cout << y; // 輸出(4) 中的 y (=7.5) } return 0; } // C = 'P'; 錯誤! 此處已無法取用 (3) 中的 C
實例介紹 • 為了方便對照,我們仍然保留16.2節問題內的First.h,FncLib.cpp和Test.cpp三個檔案的名稱: • 檔案First.h完全沒有改變,這個檔案宣告了名稱空間NS1。 • 在檔案FncLib.cpp中,我們只保留了函數NS1::Fnc() 的實作。 • 檔案Test.cpp也只包括了名稱空間NS1成員的使用。我們在這個檔案裹面示範了using指令的使用方式。 • 本程式在 DOS 下的編譯指令仍然為: bcc32 Test FncLib
範例程式 檔案 First.h // First.h #ifndef FIRST_H #define FIRST_H namespaceNS1 // 宣告名稱空間 NS1 { const double Gain = 5.9; struct Member { char Name[20]; int Phone; }; double Fnc(double); } #endif
範例程式 檔案 FncLib.cpp // FncLib.cpp #include "First.h" // 引入NS1的宣告 // 定義函數 NS1::Fnc() double NS1::Fnc(double x) {return x * NS1::Gain;}
範例程式 檔案 Array2Fnc.cpp // Test.cpp #include <iostream> using namespace std; #include "First.h" // 引入NS1的宣告 // 名稱空間 NS1的 using 指令 using namespaceNS1; // ----主程式----------------------- int main() { Member Ma={"George Wang", 23246352}; Member Mb={"Peter White", 34536767}; cout << "Name of Ma is " << Ma.Name << endl; cout << "Phone number of Mb is " << Mb.Phone << endl;; cout << "Gain is: " << Gain << endl; cout << "Fnc(2.5) is: " << Fnc(2.5) << endl; return 0; }
使用「using宣告」(using declaration) • 「using宣告」可以選擇性的宣告名稱空間中的成員。例如: namespace NS1// 宣告名稱空間 NS1 { const double Gain = 5.9; double A, B, C; void Fnc1(double); void Fnc2(double); void Fnc3(double); void Fnc4(double); } namespace NS2// 宣告名稱空間 NS2 { const double Gain = 5.9; double A, B, E; void Fnc3(double); void Fnc4(double); void Fnc5(double); void Fnc6(double); }
使用「using宣告」 • 在NS1和NS2這兩個名稱空間中,變數A,B,和函數Fnc3()、Fnc4()同名。如果我們使用以下的using宣告: using NS1::A; using NS1::Fnc2; using NS1::Fnc3; using NS2::B; using NS1::Fnc5; 就可以選擇性的直接取用NS1中的變數A和函數Fnc2()、Fnc3(),以及NS2中的變數B和函數Fnc5()。
標準名稱空間 • C++ 的標準程式庫 (the C++ Standard Library) 將所有的程式庫成員都含括在簡稱為std的名稱空間裏面: 資料流程式庫 <fstream>, 輸出格式程式庫 <iomanip>,以及計時程式庫 <ctime> 等等都是。 • 能夠以單一名稱空間std涵蓋這些程式庫的原因,是因為名稱空間具有可以用累積的方式逐步加入的特性。
使用cout和endl的三種using敘述語法 (1/3) (1) 使用「using宣告」 #include <iostream> using std::cout; using std::endl; cout << 3.5 << endl;
使用cout和endl的三種using敘述語法 (2/3) (2) 使用「using指令」 #include <iostream> using namespace std; cout << 3.5 << endl;
使用cout和endl的三種using敘述語法 (3/3) (3) 使用資格修飾子 (qualifier) #include <iostream> std::cout << 3.5 << std::endl;
未命名的名稱空間 (unnamed namespace) • 用來限制全域變數 (global variables) 的範圍,使它只能在原有檔案內存取,無法跨越到另一個檔案來使用。例如: namespace { double GainL=5.6; // GainL 被限制 double FncA(double x) // FncA() 被限制 {return x*GainL;} } • 在其它檔案中,即使做了 extern int GainL; 的宣告也無法存取不具名的名稱空間中的任何成員。
範例程式 FncLib.cpp • 這個程式區分為FncLib.cpp和Check.cpp兩個檔案。 • 在檔案FncLib.cpp中,我們定義了函數FncB()。 • 在檔案Check.cpp中,我們以不具名的名稱空間限制了GainL和FncA() 只能在此檔案中使用。
範例程式 檔案 FncLib.cpp // FncLib.cpp // 定義函數 FncB() extern double GainG; // 取用Check.cpp 中的GainG // extern double GainL; // 錯誤! 此處已無法取用 GainL double FncB(double x) { double y = x*GainG; // double y = x*GainL // 錯誤!此處已無法取用 GainL return y; }
範例程式 檔案 Check.cpp // Check.cpp #include <iostream> using namespace std; namespace// 未命名的名稱空間 { double GainL=5.6; // GainL 被限制 double FncA(double x) // FncA() 被限制 {return x*GainL;} } double GainG = 3.8; // 全域常數 GainG double FncB(double); // FncB() 的原型 // ----主程式----------------------- int main() { cout << “FncA(5)是: ” << FncA(5) << endl; // 呼叫 FncA() cout << “FncB(5)是: ” << FncB(5) << endl; // 呼叫 FncB() return 0; }