在C/C++的做用域中,講究一個就近原則。也就是說,在局部和全局都對某個變量進行了定義時,咱們的訪問/調用會去操做 「最近」 的那個變量!例以下面的例子:ios
#include <iostream> using std::cout; using std::endl; int atk = 200; void test() { int atk = 100; cout << "Local atk = " << atk << endl; } int main() { test(); return 0; }
你們能夠先猜一下運行的結果?沒錯,就是100!這就是體現了剛剛提到的 「就近原則」。那麼,若是在這種狀況下,咱們仍是想要去讀甚至寫全局的變量atk呢?怎麼辦呢?看看下面的程序:程序員
#include <iostream> using std::cout; using std::endl; int atk = 200; void test() { int atk = 100; cout << "Local atk = " << atk << endl; cout << "Global at = " << ::atk << endl; ::atk = 800; cout << "Global at = " << ::atk << endl; } int main() { test(); return 0; }
而後再看一下運行效果:編程
這就是雙冒號運算符,改變做用域的效果。在沒有指定命名空間的時候,做用域就是全局做用域!ide
所謂命名空間,是管理命名列表而生的。之因此須要對命名進行管理,那是爲了在 「迭代開發」 或者是多人 「合做開發」 中,避免 「命名衝突」 的危害!函數
如何調用命名空間的變量和函數呢?那就是用雙冒號做用域限定符!看一個需求:假設在遊戲開發中,先開發了英雄露娜的操做,例如:***、大招;隨後又開發了英雄瀾的操做,其中也有***和大招,請問怎麼實現比較好呢?給出示例程序:測試
(1)先看看開發Lunar的過程spa
頭文件(實現定義,注意定義就要在命名空間裏定義):設計
#pragma once #ifndef __LUNAR__ #define __LUNAR__ #include <iostream> using namespace std; namespace lunar { void goAtk(); void goUltimate(); } #endif
CPP文件(負責實現):code
#include "Lunar.h" void lunar::goAtk() { cout << "Lunar 釋放***!" << endl; } void lunar::goUltimate() { cout << "Lunar 釋放大招!" << endl; }
(2)隨後開發Lan的過程blog
頭文件:
#pragma once #ifndef __LAN__ #define __LAN__ #include <iostream> using namespace std; namespace lan { void goAtk(); void goUltimate(); } #endif
CPP文件:
#include "Lan.h" void lan::goAtk() { cout << "Lan 釋放***!" << endl; } void lan::goUltimate() { cout << "Lan 釋放大招!" << endl; }
總結命名空間:
命名空間能夠存放C++任何名稱,例如變量、結構體、類、函數等等……,而且,命名空間無需加分號!可是命名空間必須放到全局做用域!不能夠寫入任何一個函數、類!
舉一個更簡單的例子,要訪問兩個命名空間的變量和函數:
#include <iostream> using std::cout; using std::endl; namespace ns1 { int a; void func(int a) { cout << a << endl; } } namespace ns2 { int a; void func(int a) { cout << a << endl; } } int main() { ns1::a = 100; ns2::a = 200; ns1::func(ns2::a); ns2::func(ns1::a); return 0; }
它輸出的結果,就顯而易見了,是:
200
100
在上面,咱們瞭解了,命名空間的設計目的、設計場景。以及以遊戲的迭代開發來了解命名空間的使用!以及簡單的例子,瞭解到命名空間能夠加入任意C++中的名稱元素(變量、函數、類、結構體等……)。可是,命名空間仍是其餘的一些有用的技法:
以遊戲開發爲例,瀾這個英雄以前有普通***、放大招的功能,如今要更新一個回城的功能。而此前的代碼不想改動了,能否擴充命名空間呢?以下:
新的的頭文件:
#pragma once #ifndef __LAN__ #define __LAN__ #include <iostream> using namespace std; namespace lan { void goAtk(); void goUltimate(); } namespace lan { void goHome(); } #endif
新的CPP文件:
#include "Lan.h" void lan::goAtk() { cout << "Lan 釋放***!" << endl; } void lan::goUltimate() { cout << "Lan 釋放大招!" << endl; } void lan::goHome() { cout << "Lan 回城!" << endl; }
測試程序:
#include "Lan.h" int main() { lan::goAtk(); lan::goUltimate(); lan::goHome(); return 0; }
運行後的結果:
總結:
命名空間能夠擴展,每次擴展不是覆蓋原有的命名空間,而是把舊的命名空間和新的命名空間合併!
這個技法其實沒啥大用,就怕有的程序員使用了,而你不知道,就會踩坑!
若是命名空間寫爲:
namespace { int a, b; }
實際上等價於在當前文件,寫了:
static int a; static int b;
就徹底等價於靜態變量/函數,意味着只能在當前文件裏使用了!好比下面的程序:
#include <iostream> using namespace std; namespace { void sayHello() { cout << "Hello World" << endl; } } int main() { sayHello(); return 0; }
運行的結果是:
命名空間是能夠嵌套的!這通常得是很大的項目了!不然,其實命名空間自己也是一個比較大,我的認爲是 「僅次於全局做用域」 的存在了!例如:
#include <iostream> using namespace std; namespace ns1 { int a = 1; namespace ns2 { int a = 2; } } int main() { cout << ns1::a << ' ' << ns1::ns2::a << endl; return 0; }
運行的結果是:
這個其實很簡單,只須要用一個新的namespace空間名去賦值一箇舊的命名空間便可,例如:
#include <iostream> using namespace std; namespace ns1 { int a = 1; namespace ns2 { int a = 2; } } int main() { namespace newNS = ns1; cout << newNS::a << ' ' << ns1::ns2::a << endl; return 0; }
而後輸出的結果,和上一個技法的例子,輸出一致!
若是用 #define,則,其規則是:
#define 別名 所表明的名稱
若是用 typedef,則,其規則是:
typedef 所表明的名稱 別名
例如,分別用 #define 和 typedef 來重命名 unsigned int 和 long long,示例以下:
#include <iostream> #define uInt unsigned int typedef long long ll; int main() { uInt u = 102u; std::cout << u << std::endl; ll l = 999999999999; std::cout << l << std::endl; return 0; }
輸出的結果是:
102
999999999999
若是用using來聲明類型別名,則,其規則是:
using 別名 = 所表明的名稱;
舉個例子:
#include <iostream> using uInt = unsigned int; using ll = long long ; int main() { uInt u = 102u; std::cout << u << std::endl; ll l = 999999999999; std::cout << l << std::endl; return 0; }
其輸出的結果,和上面的一致,都是:
102
999999999999
用using能夠用來聲明變量和函數,先看一個簡單程序:
#include <iostream> int a = 10; namespace ns { int a = 20; } void test() { int a = 30; std::cout << a << std::endl; } int main() { test(); return 0; }
其輸出結果是:
30
這是因爲就近原則,再也不解釋了~
而後,若是想要使用命名空間 ns 的變量 a 呢?能夠有這種作法:
#include <iostream> int a = 10; namespace ns { int a = 20; } void test() { int a = 30; using ns::a; std::cout << a << std::endl; } int main() { test(); return 0; }
可是這樣會帶來報錯:
緣由很重要:
using 進行聲明的時候,會產生二義性!意味着會和當前做用域其餘同名變量發送衝突!換句話說,using的聲明和普通的聲明,優先級一致!此時,編譯器就不知道你究竟是要訪問誰了?!
聽起來很高級,其實就是爲編譯器補充了一個須要編譯的塊,這個塊能夠理解成一個盒子,盒子裏包裝這一個命名空間的所有內容!好比:
#include <iostream> int a = 10; namespace ns { int a = 20; } void test() { int a = 30; using namespace ns; std::cout << ns::a << std::endl; } int main() { test(); return 0; }
此時的輸出結果,是:
20
須要注意的是:
編譯了命名空間,只是讓編譯器對命名空間的內容完成編譯,而其優先級是 低於 當前做用域的聲明的!因而,仍是會進行就近原則!可是,咱們爲了可以輸出命名空間的變量,就和我們本文最開始講的那樣,顯式調用命名空間的變量!其中關鍵的程序是:
using namespace ns; std::cout << ns::a << std::endl;