????????
目錄
1、namespace的重要性
2、?namespace的定義及作用
2.1 作用域限定符
?3、命名空間域與全局域的關系
4、命名空間的嵌套
?5、展開命名空間的方法
5.1 特定展開
5.1 部分展開
5.2 全部展開
結語:
前言:
????????C++作為c語言的“升級版”,其在語法上相對于c語言有了諸多升級、優化,比如在C++中有一個全新的概念:命名空間(namespace)。在使用C++時,該語法很好的解決了對標識符命名重名的問題。
1、namespace的重要性
? ? ? ? 在使用c語言寫代碼時,常常會遇到標識符命名重名的問題。比如我們自己寫了一個函數,該函數名可能與庫函數中的某個函數發生重名,或者與他人一起寫項目時,也存在與他人代碼中的標識符同名的現象,然而以上情況的解決方法只有對標識符進行改名。
? ? ? ? 舉例說明:
#include <stdio.h>
#include <time.h>
int time = 12;int main()
{printf("%d\n", time);return 0;
}
//程序編譯時會報錯,原因是預處理階段會展開全部的頭文件(.h文件)
//被展開頭文件里面的內容是具有“全局性的”,即全局都能使用里面的內容
//然而time.h的文件中存在一個名為time的函數
//在編譯階段,編譯器會發現全局中有兩個time的名稱,并且報錯
? ? ? ? 因此針對重定義、重命名的這類問題,C++就提出了一個新的概念namespace。
2、?namespace的定義及作用
????????namespace又稱命名空間,他是一塊獨立于全局范圍內的區域,在namespace區域中定義各種標識符的名稱和全局中是分割開的,換句話說就是對命名空間內的標識符名稱進行本地化管理,這樣就不會與全局作用域中的同名標識符起沖突了。
? ? ? ? 比如,創建兩個頭文件first.h和second.h,并且把這兩個頭文件都包含到主函數文件main.cpp中:
//first.h文件:
#pragma once
int a = 10;//second.h文件:
#pragma once
int a = 101;//main.cpp文件:
//包含上述兩個.h文件
#include"first.h"
#include"second.h"
#include<stdio.h>int main()
{printf("%d ",a);//a重定義了return 0;
}//會報錯:a重定義
? ? ? ? 上述代碼若運行,則會發生編譯報錯,原因就是再展開這兩個頭文件后,會出現兩個a多重定義的報錯。這時候可以將其中一個頭文件的變量a換另一個名稱,或者main.cpp中只包含其中一個頭文件。但是如果這兩個文件都要包含而且也不想對變量a的名稱進行更改,那么只能用namespace將兩個頭文件下的變量a存到命名空間內。
2.1 作用域限定符
? ? ? ? 使用namespace進行對上述代碼的優化:
//first.h文件:
#pragma once
namespace first//namespace用法:namespace+自定義名稱
{int a = 10;}//second.h文件:
#pragma once
namespace second
{int a = 101;}//main.cpp文件:
//包含上述兩個.h文件
#include"first.h"
#include"second.h"
#include<stdio.h>int main()
{printf("%d ", second::a);//::表示作用域限定符,左邊跟作用域名稱return 0;
}//會報錯:a重定義
? ? ? ? 上述代碼則將兩個頭文件下的變量a都放在了兩塊不一樣的命名空間內,這樣一來他們的名稱就不會互相干涉了,只不過在使用變量a的時候要多一個步驟:使用作用域限定符去特指的命名空間查找。因為編譯器也不知道程序員需要用哪個a,所以程序員需要在使用的a的左邊加上“::”符號,并且在“::”符號的左邊加上命名空間的名稱,這樣就可以精確的使用某個命名空間里的內容了,也稱展開命名空間。
? ? ? ? 上述代碼運行結果:
?3、命名空間域與全局域的關系
? ? ? ? 如果上文中的代碼沒有對a使用“second::”,會出現什么樣的后果呢?
? ? ? ? 可以發現編譯器顯示找不到變量a了,因為編譯器查找的順序是先找局部、再找全局,并不會自動的去命名空間內查找,所以全局域和命名空間域是分開的兩個區域。因此在上述代碼中,當頭文件里的變量a被存放在命名空間中,可以理解為該變量從全局域被移動至命名空間域。
? ? ? ? 關系圖:
? ? ? ? 比如全局域和局部域都有一個名為a的變量,如果編譯器在局部域中就找到了a,則編譯器會直接調用該a的值,并且也不會去全局域中查找,用上述代碼進行變形當作例子:
#include"first.h"
#include"second.h"
#include<stdio.h>int a = 1021;//全局變量int main()
{int a = 22;//局部變量printf("%d ", a);return 0;
}
? ? ? ? ?運行結果:
????????可以看到編譯器直接選用了局部變量a作為打印結果。并且我們新加了全局變量int a=1021,編譯器也沒有報重命名的錯誤,說明全局域和命名空間域是分開的的兩個區域,在全局域中定義了一個a,則命名空間域也能使用a的名稱。
4、命名空間的嵌套
? ? ? ? ?命名空間的嵌套就是在該空間內在創建一個命名空間,一般是防止最外層命名空間的名稱與別的空間同名,寫法如下:
//first.h
#pragma once
namespace first
{namespace A{int a = 10;}
}//second.h
#pragma once
namespace first//假設兩個頭文件下的第一層空間重名
{namespace B//則需要第二層空間來區別a變量{int a = 101;}
}//main.cpp
#include"first.h"
#include"second.h"
#include<stdio.h>int a = 1021;int main()
{printf("%d\n", first::A::a);printf("%d\n", first::B::a);return 0;
}
? ? ? ? 運行結果:
?5、展開命名空間的方法
? ? ? ? 展開命名空間就是從命名空間內讀取內容,上文提到的作用域限定符就是其中的一個辦法,但是如果讀取大量的內容就會很麻煩,因為只要是每一次讀取都要加上作用域限定符,會很繁瑣。因此另兩種方法是部分展開和全部展開。
5.1 特定展開
? ? ? ? 特定展開就是上文的展開方式,既:空間名稱::變量名稱。值得一提的是,使用特定展開時,編譯器不會去局部和全局找,而是直接到命名空間內找,因此就算全局也有與該變量一模一樣的名稱,也不會報錯,而且編譯器還是會調用命名空間內的變量。
? ? ? ? 特定展開代碼如下:
//first.h
#pragma once
namespace first
{int a = 10;
}//second.h
#pragma once
namespace second
{int a = 101;
}#include"first.h"
#include"second.h"
#include<stdio.h>
int a = 1021;int main()
{printf("%d\n", first::a);//編譯器會調用first文件中的a,而不是調用全局a=1021的areturn 0;
}
?????????運行結果:
5.1 部分展開
? ? ? ? 在全局處使用using+空間名稱::變量名稱。部分展開與特定展開就不一樣了,部分展開是把要調用的變量移動到全局域中,然后編譯器在全局域中找到該變量,并不是讓編譯器指定到該空間去找,因此要保證全局中不能出現與該變量一樣的名稱,不然會報錯。
? ? ? ? 部分展開邏輯圖如下:
????????具體代碼如下:
//first.h
#pragma once
namespace first
{int a = 10;
}//second.h
#pragma once
namespace second
{int a = 101;
}//main.cpp
#include"first.h"
#include"second.h"
#include<stdio.h>
using first::a;//展開first空間并且只調用a
//int a = 1021;//注意這時候first.h里的變量a屬于全局變量了,不能再定義額外名稱的a的變量int main()
{printf("%d\n", a);printf("%d\n", a);return 0;
}
? ? ? ? 運行結果:
???????? 在全局處加上了using first::a,之后所有需要調用a變量的代碼前面都不需要再加作用域限定符了。但是僅僅限于變量a不用加限定符,如果要調用first空間內其他的變量還是要加作用域限定符的,因此又引出一個新的概念:全部展開,全部展開某個命名空間,則后續的代碼可以不加限定符直接調用該空間內的所有內容。
5.2 全部展開
? ? ? ? 在全局處加上using+namespace+要展開空間的名稱,既可對該空間進行全部展開。全局展開也同部分展開邏輯一樣,全局展開相當于把該空間里的所有內容都移到全局域中,因此全局域中不能出現與該空間內有標識符名稱相同的情況。
? ? ? ? 全部展開代碼如下:
//first.h
#pragma once
namespace first
{int a = 10;int b = 123;int c = 456;
}//second.h
#pragma once
namespace second
{int a = 101;
}//main.cpp
#include"first.h"
#include"second.h"
#include<stdio.h>
using namespace first;
//int a = 1021;int main()
{printf("%d\n", a);printf("%d\n", b);printf("%d\n", c);return 0;
}
? ? ? ? 運行結果:
? ? ? ? 從結果來看,當全部展開first空間后,可以隨意使用該空間的內容而且無需添加任何條件。?
結語:
????????以上就是關于C++中命名空間的介紹,對于命名空間的全部展開其實在一般的情況下是不推薦的,因為全部展開意味著空間內的所有內容都變成了全局的,很容易發生重名,也就失去了命名空間防止重名的意義。
????????最后希望本文可以給你帶來更多的收獲,如果本文對你起到了幫助,希望可以動動小指頭幫忙點贊👍+關注😎+收藏👌!如果有遺漏或者有誤的地方歡迎大家在評論區補充~!!謝謝大家!!( ̄︶ ̄)↗