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


