---
# System prepended metadata

title: Namespace 命名空間
tags: [東華大學資管系, 資管經驗分享, 東華大學, 基本程式概念, 大一程設-下]

---

---
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;
```

![](https://i.imgur.com/v2Fevua.png)

他說 Student 是模糊的，NTU 也有一種 Student，代表現在電腦不知道該用哪一個了，所以這就是 namespace 好用的地方，能夠明確點出我是誰。

所以當今天類別撞名，就算你使用了 using 你還是得寫 `NDHU::Student` / `NTU::Stduent` 來明確讓編譯器知道誰是誰。

而透過上面的例子，你大概能夠在心裡構建出這樣一張圖。

![](https://i.imgur.com/6atULID.png)

兩個命名空間互不相干，非常簡潔明瞭，但你如果想要在 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)
