--- GA: G-RZYLL0RZGV --- ###### tags: `大一程設-下` `東華大學` `東華大學資管系` `基本程式概念` `資管經驗分享` Namespace 命名空間 === [TOC] ## 前言 相信寫過了這麼多程式之後,會發現我們的程式總是跟 main 綁再一起,透過學到了上一章的拆分檔案後,我們也學會了怎麼引入自己撰寫的 library,如果今天程式有非常多的 library,想必我們的專案會越來越大,而在專案擴充的過程當中,我們不免會遇到物件、函式可能會想取同名字的情況發生,但他們想觸發的事情可能或多或少會有不同,依靠一般的 function overloading 可能有時候沒有辦法完全的解決我們「撞名」的問題,甚至是想要取同名字的變數也有可能發生。 namespace 就是為了解決這樣撞名的問題而被設計出來的一種工具。 單純看這樣介紹或許沒辦法非常直白的了解,我們馬上開始吧! ## namespace 我們假設以下一個情境來邊講述 namespace 吧! 假設今天有兩間學校,兩間學校都有非常多的學生,而針對學生呢,學校的資料庫會儲存以下的資料。 * 名字(name) * 年紀(age) * 身高(height) * 體重(weight) * 連絡電話(telephone) * 手機(cellphone) 如果依照我們現在所學,我們可能很快會寫這樣的類別。 ```cpp= class Student{ string name; int age; double height; double weight; string telephone; string cellphone; }; ``` 看起來非常正確簡單明瞭,然而經過探查我們發現,兩間學校針對資料變數型態的方式居然有些不一樣,但變數的名字居然都要求要一樣,那你可能會說,那我再宣告一個 class 就好了,這確實是一個非常有效的解法,那如果今天有第三個學校你可能也會選擇再加一個 class。 接著我們在擴充一下我們的需求,今天學校說除了記錄學生之外,希望我們也能夠幫教職員做資料的儲存,三間學校可能有三種教職員的儲存方式,你可能又會說,那建立三個類別分別給三間學校的教職員吧。 所以你可能會寫出 StudentA、EmployeeA、StudentB、EmployeeB、StudentC、EmployeeC,六個類別,別說了,看過去真的醜到開花。 今天在這樣的需求下我們可以知道,每間學校可能針對資料各有各的儲存方式,拆分成不同的類別確實也是正確的方式,基於這樣的基礎,我們其實能夠加上 namespace 來讓我們程式碼寫得看起來更好看一點。 ### 基本語法 ```cpp= namespace space_name{ //something you define... } ``` 你可以在 namespace 裡面寫所有你需要的東西。而定義在 namespace 裡面的東西,只隸屬於此命名空間,其他人如果要使用就必須取用他。 ### 範例說明 ```cpp= namespace NDHU{ class Student{ string name; int age; double height; double weight; string telephone; string cellphone; }; class Employee{ string name; int age; double height; double weight; string telephone; string cellphone; }; } ``` 這樣一看就非常明瞭,只要是東華大學下的學生跟教職員,我們會如此的定義。也不用因為類別變多就取醜醜的什麼 ABC。而如果類別的細項有所不同也沒關係,名字也是可以取好好的。 ```cpp= namespace NTU{ class Student{ string name; int age; double height; double weight; char telephone[11] ; char cellphone[11]; }; class Employee{ string name; int age; double height; double weight; char telephone[11] ; char cellphone[11]; } } ``` 那你可能會說,我們前面不是學了拆分檔案嗎,那我把不同的類別拆分到各自的檔案不就沒事了嗎? 非常好的解法,但如果今天同樣的一份程式需要 include 上面這兩間學校,在沒有 namespace 的支持下進行這樣類別的命名的時候,你會有兩個 Student 跟 Employee,你的程式碼會不知道要用哪一個類別。 > 你如果感到疑惑一定是上一篇筆記沒認真看 > [name=Orange] 但如果今天我們有了 namespace 的支持,我們就可以明確地說,今天我要使用 NDHU 的 Stduent,或是我要使用 NTU 的 Employee。諸如此類。 ## 如何使用 我們馬上來使用命名空間吧。 在看例子之前,我們先來瞭解基本語法,如果 namespace 跟 main 放在同一個檔案裡面,不用 include 標頭檔,也不用寫 using。 ```cpp= #include <iostream> using namespace std; namespace NDHU{ class Student{ public: string name; int age; double height; double weight; string telephone; string cellphone; }; class Employee{ public: string name; int age; double height; double weight; string telephone; string cellphone; }; } int main(){ NDHU::Student ndhu_1; ndhu_1.age = 20; ndhu1.name = "bob"; return 0; } ``` 你可以看到第 18 行,我們看到了久違的 `::` 符號,還記得我們說過我們可以叫他 `的`,所以不難理解,我們這邊實體化了一個 Student,隸屬於 NDHU,所以就是 NDHU 的 Student。 不用被混淆,`NDHU::Student` 就是物件宣告時的類別型態而已。剩下的取屬性,取用方法都跟我們過去學的一樣。 ## namespace 在不同的檔案中 透過學了上一章的拆分檔案,我們來把 namespace 給拆分出去吧,都寫在 main.cpp 裡面太佔空間了。 我們假設現在有下面幾個重要的檔案。為了減少篇幅,全部的屬性我都設 public。 * main.cpp * ndhu.h * ndhu.cpp * ntu.h * ntu.cpp ndhu.h ```cpp= #include <iostream> using namespace std; namespace NDHU{ class Student{ public: string name; int age; double height; double weight; string telephone; string cellphone; void play(); void run(); void study(); }; class Employee{ public: string name; int age; double height; double weight; string telephone; string cellphone; void work(); void walk(); void teach(); }; } ``` ndhu.cpp ```cpp= #include "ndhu.h" using namespace std; void NDHU::Student::play(){ cout << this->name << " is playing!" << endl; } void NDHU::Student::run(){ cout << this->name << " is running!" << endl; } void NDHU::Student::study(){ cout << this->name << " is studying!" << endl; } //以下 Employee 省略 ``` ntu.h ```cpp= #include <iostream> using namespace std; namespace NTU{ class Student{ public: string name; int age; double height; double weight; char telephone[11]; char cellphone[11]; void play(); void run(); void study(); void fly(); }; class Employee{ public: string name; int age; double height; double weight; char telephone[11]; char cellphone[11]; void work(); void walk(); void teach(); }; } ``` ntu.cpp ```cpp= #include "ntu.h" using namespace std; void NTU::Student::play(){ cout << this->name << " is playing!" << endl; } void NTU::Student::run(){ cout << this->name << " is running!" << endl; } void NTU::Student::study(){ cout << this->name << " is studying!" << endl; } void NTU::Student::fly(){ cout << this->name << " is flying!" << endl; } //以下 Employee 省略 ``` NTU 跟 NDHU 兩個最大的差異就是 NTU 的 Student 我加了一個 fly,以及兩者 phone 的基礎型態不同,學過繼承的你可能有另外的解決方式,所以我想說,解沒有絕對,完全看你怎麼組合你的工具!這邊只是為了仔細說明 namespace。 而你也可以看到在 `ndhu.cpp` 跟 `ntu.cpp` 我們寫了 `NDHU::Stduent::play()` / `NTU::Student::play()`,主要就是在定義該類別底下的 function。 那如果你覺得這樣寫很麻煩,就要來使用 using 了。 相信 `using namespace std` 這句話困擾大家很長一段時間,我們先透過 ndhu 跟 ntu 來看看這奇怪的東西。 我們把 ndhu.cpp 修改一下。 ```cpp= #include "ndhu.h" using namespace std; using namespace NDHU; void Student::play(){ cout << this->name << " is playing!" << endl; } void Student::run(){ cout << this->name << " is running!" << endl; } void Student::study(){ cout << this->name << " is studying!" << endl; } //以下 Employee 省略 ``` 我們在第三行加了 `using namespace NDHU`,而在這個 namespace 底下有兩個類別,因此在這個 cpp 檔裡面,我們可以不用再寫那麼長的 `NDHU::Student` 這樣的前綴。 至於剩下的 include 就跟前面的筆記說的一模一樣,只是在過往學的東西再套上 namespace 這個新東西而已。 所以究竟 `using namespace std` 是甚麼,不難想像,有一個叫做 std 的命名空間,裡面有我們想要用的工具,所以我們需要 using 他,我們最常使用的 cin、cout 其實就被定義在裡面,所以如果今天我們沒有 using std 這個命名空間,我們的 cin、cout 該怎麼寫? 就是 `std::cin`、`std::cout`。 ```cpp= #include <iostream> int main(){ int a = 0; std::cin >> a; std::cout << a; } ``` > 有忽然覺得茅塞頓開嗎?希望有拉XD > [name=Orange] 接著我們來看看最後的 main.cpp,假設我們想同時使用 NDHU 跟 NTU,按照前面學的,你可能會寫。 ```cpp= #include <iostream> #include "ndhu.h" #include "ntu.h" using namespace NDHU; using namespace NTU; using namespace std; ``` 然而今天 NDHU 跟 NTU 這兩個命名空間都有 Student 跟 Employee,如果你直接宣告下面這樣會報錯。 ```cpp= Student ndhu_1; Student ntu_1; ```  他說 Student 是模糊的,NTU 也有一種 Student,代表現在電腦不知道該用哪一個了,所以這就是 namespace 好用的地方,能夠明確點出我是誰。 所以當今天類別撞名,就算你使用了 using 你還是得寫 `NDHU::Student` / `NTU::Stduent` 來明確讓編譯器知道誰是誰。 而透過上面的例子,你大概能夠在心裡構建出這樣一張圖。  兩個命名空間互不相干,非常簡潔明瞭,但你如果想要在 A 命名空間使用 B 命名空間的內容,其實也可以在 A 裡面透過 `B::` 去取用你要的東西。 ## Nested Namespace 巢狀命名空間 今天如果需求更上升,需要劃分更明確的需求的時候,namespace 包著其他的 namespace 也是有可能的。 ### nested namespace syntax ```cpp= namespace COMPANY{ namespace Finance{...} namespace Engineering{...} namespace SocialMarketing{...} } ``` 一個公司內部可能有多個部門,每個部門各司其職,份內事彼此互不相干,但如果有共同的東西想要被大家一起共用,你可以寫在大家都能夠存取到的地方。 > 巢狀並沒有限制幾層,想怎麼寫就怎麼寫,但要寫得有效率。 > [name=Orange] ```cpp= namespace TSMC{ const string boss = "張忠謀"; // 台積電的老闆是張忠謀 const int age = 35; // 台積電今年 35 歲 void drinkCoffee(){...} // 台積電每個部門的人都可以喝咖啡 void takeDayOff(){...} // 台積電每個人都可以請假 namespace Finance{...} namespace Engineering{...} namespace SocialMarketing{...} } ``` 像上面這樣,Boss、age 跟兩個 function 是不論部門大家都可以用的,但如果今天 Engineering 需要用到 Finance 內的一些工具,就要在 Engineering 內寫 `Finance::`,簡單明瞭。 ## 總結 這篇筆記主要是讓大家理解 namespace 的運作方式 namespace 讓我們能夠把程式碼區分得更明確,讓彼此不相干的功能不會互相影響,然而只單純使用 namespace 有時候不會完全的讓程式碼簡潔,必要的時候是要結合繼承、運算子多載、函式多載等我們過去學過的功能來讓它的效益最大化。 當然更複雜的還有範本(template)、多型(polymorphism),與此結合程式碼能夠更加完善。 ## Reference * [Namespaces](https://en.cppreference.com/w/cpp/language/namespace) * [簡介名稱空間](https://openhome.cc/Gossip/CppGossip/Namespace.html) * [C++ 命名空间](https://www.runoob.com/cplusplus/cpp-namespaces.html) * [命名空間 (C++) Microsoft](https://docs.microsoft.com/zh-tw/cpp/cpp/namespaces-cpp?view=msvc-170)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.