【C++ 筆記】命名空間(Namespace) - part 30 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 可喜可賀可喜可賀,恭喜本系列終於來到 part 30 啦! 簡介(Introduction) --- 我們最熟悉的命名空間不外乎就是: ```cpp using namespace std; ``` 為什麼要用這個?因為我們不想要打那麼多字,每次打一行程式碼都要加上 `std::` 不是挺麻煩的嗎? ```cpp= std::cout std::endl std::vector <int> a; ``` 但其實除了圖方便以外,命名空間也有其他功能。 假設班上有兩名同學,他們都叫陳柏鈞,為了要區分他們兩個,我們就必定要用到額外的訊息去區分,如一個比較有錢,一個普通,或是一個帥,一個醜等等。 在 C++ 中,假設有個函數叫做 func(),而在另一個 library 也叫做 func(),此時就會有命名衝突(Name conflicts)的問題,因此 namespace 就誕生了。 ### namespace 是啥? 命名空間(namespace)是一種將程式碼邏輯上分組的方法,用以避免不同程式庫或模組之間的名稱衝突。namespace 也提供了一種將相關識別字(identifier)(如變數、函數和類別)分組到單一名稱下的方法。 透過 namespace,我們可以將函數、變數、類別等符號規劃到各自獨立的區域,強化程式碼的可讀性與可維護性。 ### 我們要 namespace 幹嘛? 1. 解決命名衝突(Name Conflicts) 2. 模組化(Modularity) 模組化是一個蠻重要的觀念,比如在開發遊戲時,毫無概念的直接將所有物件都寫在同一個檔案內,那就會導致可讀性大大降低。因此可以透過分類的方式,如將玩家的相關邏輯都寫在 `player.cpp` 裡面,怪物的邏輯則為 `monster.cpp` 等,而主程式的檔案叫做 `main.cpp`,負責引入 `player.cpp`、`monster.cpp` 等檔案。 定義 namespace --- 以 `namespace` 作為關鍵字,後面的 name 為自定義,如同變數名一樣。 要注意的是右括號 `}` 沒有分號作結。 ```cpp= namespace name { // type1 mamber1 // type2 mamber2 // type3 mamber3 . . . . . . } ``` 小範例: ```cpp= namespace nptu{ void departmentName(){ cout << "國立屏東大學 電腦科學與人工智慧學系" << endl; } } ``` ### 巢狀 namespace 定義 註:C++ 17 開始才可以這樣做。 ```cpp= namespace nptu{ void schoolName(){ cout << "國立屏東大學" << endl; } namespace department{ void departmentName(){ cout << "電腦科學與人工智慧學系" << endl; } } } ``` 存取 namespace 成員 --- 使用視域解析運算子(Scope resolution operator)`::`,就是兩個半形冒號。 `name::member` - name:命名空間的名稱。 - member:內部成員,無論是變數、函數等皆可。 如下範例: ```cpp= #include <iostream> using namespace std; namespace nptu{ void departmentName(){ cout << "國立屏東大學 電腦科學與人工智慧學系" << endl; } } int main(){ nptu::departmentName(); return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 ``` ### 存取巢狀 namespace 如下所示的 `nptu::department::departmentName()`,在多套一個 `::` 就可以了。 ```cpp= #include <iostream> using namespace std; namespace nptu{ void schoolName(){ cout << "國立屏東大學" << endl; } namespace department{ void departmentName(){ cout << "電腦科學與人工智慧學系" << endl; } } } int main(){ nptu::schoolName(); nptu::department::departmentName(); return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 ``` ### using 指令(using directive) 如同最開始所說的,`using namespace std;` 可以讓我們省掉打上 `std::` 的動作。 語法:`using namespace name;`,name 是命名空間的名稱。 這個 using 的效果是「將整個命名空間內容引入當前作用域,可直接使用所有成員」。 如下範例所示: ```cpp= #include <iostream> using namespace std; namespace nptu{ void departmentName(){ cout << "國立屏東大學 電腦科學與人工智慧學系" << endl; } } using namespace nptu; int main(){ departmentName(); // 省去打 nptu:: return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 ``` ### using 宣告(using declaration) 語法:`using name::member;` - name:命名空間名稱。 - member:內部成員。 與上一個不同的是,這個語法只會引入單一成員,而不是全部的成員。 ```cpp= #include <iostream> using namespace std; namespace nptu{ void departmentName(){ cout << "國立屏東大學 電腦科學與人工智慧學系" << endl; } } using nptu::departmentName; int main(){ departmentName(); return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 ``` ### using 不能濫用 為什麼會這麼說呢?因為 using 指令會使所有成員變得可存取,這樣很容易發生命名衝突的事件,如果使用宣告方式會減少此類事件發生。 那些內建的 namespace --- ### std namespace std namespace 是 STL(standard library)的一部分,其中有一大部分的標準函數、物件和類別,如 `cin`、`cout`、`vector` 等。 這部分其實我們都很熟悉了,所以就這樣。 ### global namespace 先來看以下的例子: ```cpp= #include <iostream> using namespace std; int n = 5; int main(){ int n = 2; return 0; } ``` `int n = 5` 既沒有被 namespace 的定義給包住,也沒有放到任何函數裡面,這種就屬於 global namespace,也稱為全域變數。 另外他也是可以被 `::` 存取到的: ```cpp= #include <iostream> using namespace std; int n = 5; int main(){ int n = 2; cout << "全域 n : " << ::n << endl; cout << "局域 n : " << n << endl; return 0; } ``` Output: ``` 全域 n : 5 局域 n : 2 ``` ### extending namespace 一樣來看例子: ```cpp= #include <iostream> using namespace std; namespace nptu{ void schoolName(){ cout << "國立屏東大學" << endl; } } namespace nptu{ void departmentName(){ cout << "電腦科學與人工智慧學系" << endl; } } int main(){ nptu::schoolName(); nptu::departmentName(); return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 ``` 正如其名,延伸的命名空間,可以幫這個同名的命名空間新增函數、變數等,為這個命名空間做一個延伸,不管他是在哪個地方定義的。 建立 namespace 的別名 --- 語法:`namespace nn = namespace_name;` - namespace_name:命名空間的原名。 - nn:命名空間的別名。 那既然都說是命名空間的別名了,所以原名也可以直接使用。 ```cpp= #include <iostream> using namespace std; namespace NationalPingTungUniversity{ void schoolName(){ cout << "國立屏東大學" << endl; } void departmentName(){ cout << "電腦科學與人工智慧學系" << endl; } } namespace nptu = NationalPingTungUniversity; int main(){ nptu::schoolName(); nptu::departmentName(); NationalPingTungUniversity::schoolName(); NationalPingTungUniversity::departmentName(); return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 國立屏東大學 電腦科學與人工智慧學系 ``` inline namespace --- 使用 `inline` 關鍵字加在 `namespace` 前面,裡面的成員不用 `::` 就可以直接存取。 ```cpp= #include <iostream> using namespace std; inline namespace nptu{ void schoolName(){ cout << "國立屏東大學" << endl; } void departmentName(){ cout << "電腦科學與人工智慧學系" << endl; } } int main(){ schoolName(); departmentName(); return 0; } ``` Output: ``` 國立屏東大學 電腦科學與人工智慧學系 ``` 匿名命名空間(anonymous namespace) --- 所謂匿名命名空間就是沒有名字的命名空間,其內部定義的函數、變數等識別字的作用域僅限於該檔案內,其他檔案無法存取這些識別字,以達到內部連結(internal linkage)的效果。 :::info 在 C++ 中,「連結(linkage)」是指一個名稱(變數、函數等)在不同翻譯單位(translation unit)之間是否可以被辨識與共用。 ::: 註:翻譯單位是編譯過程的基本單位。`一個翻譯單位 = 一個 .cpp 檔案 + 它所 #include 的所有標頭檔內容`。 而內部連結指的是某個符號(名稱)只能在定義他的原始檔案中(翻譯單位內)被使用,其他檔案看不到也不能存取。 C-style 常見的做法是使用儲存類別 `static`,到了 C++ 就是匿名命名空間。 anonymous namespace 範例: ```cpp= #include <iostream> using namespace std; namespace{ void func(){ cout << "呵呵呵,看不到我吧" << endl; } } int main(){ func(); // 匿名命名空間內的成員可直接存取 return 0; } ``` Output: ``` 呵呵呵,看不到我吧 ``` 總結 --- ### 命名空間 (Namespace) 是什麼? 命名空間是一種將程式碼邏輯分組的機制,用來避免不同函數、變數、類別等識別字名稱衝突。 他提供一個作用域範圍,讓同名的識別字可以在不同命名空間中共存,不會互相干擾。 如:std 是 STL 的命名空間,裡面包含了 cout、vector 等標準物件和函數。 ### 為什麼需要命名空間? 避免命名衝突:如多個函數或變數重名,namespace 可區隔使用。 模組化設計:利於大型系統的架構與維護。 ### 定義命名空間 ```cpp= namespace name { // 成員:函數、變數、類別等 } ``` 右括號 `}` 後不需加分號。 可定義巢狀命名空間(C++17 起支援): ```cpp= namespace nptu { namespace department { void departmentName() { ... } } } ``` ### 存取命名空間成員 使用視域解析運算子(Scope resolution operator)`::`,如: ```cpp= nptu::departmentName(); nptu::department::departmentName(); ``` nptu 是命名空間的名稱,`::` 後面接的是內部成員。 ### using 指令與宣告 - `using namespace name;`:將整個命名空間引入當前作用域,成員可直接使用,省略 `name::`。 - `using name::member;`:只引入單一成員。 ### 內建命名空間 - std namespace:標準函式庫所在命名空間。 - global namespace:未被任何命名空間包覆的全域範圍,可用 :: 存取。 - 延伸命名空間:同一命名空間可在多處定義,成員會合併。 - 命名空間別名: - `namespace nn = namespace_name;` - inline namespace:使用 `inline` 關鍵字,裡面成員可直接存取,無須加 `::`。 - 匿名命名空間:無名的命名空間,內部成員只在該檔案可見,達到內部連結效果。 參考資料 --- [Translation units and linkage (C++) | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/cpp/program-and-linkage-cpp?view=msvc-170) [簡介名稱空間](https://openhome.cc/Gossip/CppGossip/Namespace.html) [C++ Inline Namespaces and Usage of the "using" Directive Inside Namespaces - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/cpp-inline-namespaces-and-usage-of-the-using-directive-inside-namespaces/) [Namespace in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/namespace-in-c/) [C++ 命名空间 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-namespaces.html)
×
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
.