- 🌸博主主頁:@釉色清風
- 🌸文章專欄:C++
- 🌸今日語錄:如果神明還不幫你,說明他相信你。
- 🪷文章簡介:這篇文章是結合譚浩強老師的書以及自己的理解,同時加入了一些例子,方便理解。希望對大家有幫助。
用命名空間避免命名沖突
- 🌼名字沖突問題
- 🌻例子
- 🌻名字沖突
- 🌼命名空間
- 🌻命名空間的類型
- 🌻命名空間的成員
- 🌻命名空間的使用
- 🌼使用命名空間解決名字沖突
🌼名字沖突問題
在實際生活中,一個大型的項目往往不是一個人獨立完成的,而是由若干個人合作完成的,不同的人分別完成不同的部分,最后組合成一個完整的程序。
假如不同的人分別定義了類,放在不同的頭文件中,在主文件(包含主函數的文件)需要用這些類時,就用#include指令將這些頭文件包含進來。
由于各頭文件是由不同的人設計的,有可能在不同的頭文件中用了相同的名字來命名所定義的類或函數。這樣在程序中就會出現名字沖突。
🌻例子
程序員甲在頭文件header1.h中定義了類Student和函數fun:
#pragma once
//header1.h
#include <iostream>
#include<string>
#include <cmath>
using namespace std;
class Student //聲明Student類
{
public:Student(int n, string nam, int a){num = n;name = nam;age = a;}void get_data();
private:int num;string name;int age;
};
//成員函數的定義
void Student::get_data()
{cout<< num <<"" << name << "" << age << endl;
}
//定義全局函數(即外部函數)
double fun(double a, double b)
{return sqrt(a + b);
}
在main函數所在的主文件中包含文件header1.h:
#include "header1.h"
int main()
{Student stud1(101,"Wang",18);stud1.get_data;cout<<fun(5,3)<<endl;return 0;
}
運行結果如下:
如果程序員乙寫了頭文件head2.h,在其中定義了其他類以外,還定義了類Student和函數fun,但其內容與頭文件header1.h中的Student和函數fun有所不同。
//header2.h
#pragma once
#include <string.h>
#include<cmath>
#include<iostream>
using namespace std;
class Student
{
public:Student(int n, string nam, char s)//參數與header1.h中不同{num = n;name = nam;sex = s;}void get_data();
private:int num;string name;char sex;
};//成員函數定義
void Student::get_data()
{cout << num << "" << name << "" << sex << endl;
}
//定義全局函數
double fun(double a, double b)
{return sqrt(a - b);//返回值與header1中的fun函數不同
}
......
//頭文件2可能還有其他的內容
假如主程序員在其程序中要用到header1.h中的Student和函數fun,因而在程序中包含了頭文件header1.h,同時又要用到頭文件header2.h中的一些內容,但是不知道此時header2.h中包含了與header1.h同名單內容不同的Student類和fun函數,因而又在頭文件中包含了頭文件header2.h。
主文件如下:
//main file
#include <iostream>
#include "header1.h"
#include "header2.h"int main()
{Student stud1(101,"Wang",18);stud1.get_data;cout<<fun(5,3)<<endl;return 0;
}
這時,程序編譯就會出現錯誤。
🌻名字沖突
因為在預編譯后,頭文件中的內容取代了對應的#include指令,這樣就在同一個程序文件中出現了兩個Student類和兩個fun函數,顯然是重復定義,這就是名字沖突。
名字沖突,即在同一個作用域中含有兩個或多個同名的實體。
不僅如此,在程序中往往還需要引用一些庫,包括C++編譯系統提供的庫、由軟件開發商提供的庫或者用戶自己開發的庫,為此需要包含有關的頭文件。如果在這些庫中包含有與程序的全局實體同名的實體,或者不同的庫中有相同的實體名,則在編譯時就會出現名字沖突。有人稱之為全局命名空間污染。
為了避免這類問題的出現,人們提出了許多方法,例如:將實體的名字寫得長一點(包含十幾個或幾十個字母和字符);把名字搞得特殊一些,包括一些特殊的字符;由編譯系統提供的內部全局標識符都用下劃線作為前綴,如_complex(),以避免與用戶的實體同名;由軟件開發商提供的尸體的名字用特定的字符作為前綴等。但是這樣的效果并不理想,而且增加了閱讀程序的難度,即可讀性降低了。
C語言和早期的C++語言沒有提供有效的機制來解決這個問題,沒有庫的提供者能夠建立自己的命名空間。人們希望ANSI C++ 標準庫能夠解決這個問題,提供一種機制、一種工具,使由庫的設計者命名的全局標識符能夠和程序的全局實體名以及其他類的全局標識符區別開來。
🌼命名空間
為了解決這個問題,ANSI C++增加了命名空間。
所謂命命名空間,就是一個由程序設計者命名的內存區域。程序設計者可以根據需要指定一些有名字的空間域,把一些全局實體分別放在各個命名空間中,從而與其他全局實體分離開來。
我的理解就是原來我們的全局實體變量都是“暴露出來”的,然后命名空間的作用就是分別個你需要的全局實體“圍起來”,像柵欄一樣,每一塊區域都有一個專屬于自己的名字。然后在編譯階段,就像是好多個“圍起來的柵欄”,且各不相同。
命名空間的作用是建立一些互相分隔的作用域,把一些全局實體分隔開來,以免產生名字沖突。
這里有一段很形象的描述,可以幫助大家更好地理解:
例如,某中學高三年級有3個叫李相國的學生,如果都在同一班,那么老師點名叫李相國時,3個人都站起來應答,這就是名字沖突。因為他們無法辨別老師想叫的是哪一個李相國?同名者無法互相區分。為了避免同名混淆,學校把3個同名的學生分在3個班。這樣,在小班點名叫李相國時,只會有一個人應答。也就是說,在該班的范圍內,即班作用域內名字是唯一的。如果在全校集合時校長點名,需要在全校范圍內找這個學生,要考慮的作用域是全校范圍。如果校長叫李相國,全校學生中又會有3個人一齊喊“到”,因為在同一作用域中存在3個同名學生。為了在全校范圍內區分這3名學生,即加上班名限定。這樣就不會產生混淆。
可以根據需要設置許多個命名空間,每個命名空間代表一個不同的命名空間域,不同的命名空間不能同名。這樣,可以把不同的庫中的實體放到不同的命名空間中,或者說,用不同的命名空間把不同的實體隱藏起來。過去用的全局變量可以理解為存在于全局命名空間,獨立域所有有名的命名空間之外,不是不需要namespace聲明的,實際上是由系統隱式聲明的,在該空間中有效。
🌻命名空間的類型
在聲明一個命名空間時,花括號內不僅可以包括變量,而且還可以包括一下類型:
- 變量(可以帶有初始化);
- 常量;
- 函數(可以是定義或聲明);
- 結構體;
- 類;
- 模板;
- 命名空間(在一個命名空間中又定義了一個命名空間,即嵌套的命名空間)。
🌻命名空間的成員
例如:
namespace ns1 //指定命名空間ns1
{int a;double b;
}
- ns1是命名空間的名字。
- 在花括號內,聲明的實體即為命名空間的成員,包括全局變量a和b。
- 使用a和b,需要加上命名空間和作用域分辨符"::",如ns1::a,ns1::b。
- 需要注意的是,a和b仍然是全局變量,僅僅是把他們放在了命名空間中而已。
🌻命名空間的使用
舉例如下:
namespace ns1
{const int RATE=0.08;//常量double pay;//變量double tax()//函數{return a*RATE;}namespace ns2 //嵌套命名空間{int age;}
}
輸出命名空間中ns1中成員的數據:
cout<<ns1::RATE<<endl;
cout<<ns1::pay<<endl;
cout<<ns1::tax()<<end;
cout<<ns1::ns2::age<<endl;
🌼使用命名空間解決名字沖突
聲明命名空間ns1,并在命名空間ns1中聲明Student類和定義成員函數、定義fun函數。
#pragma once
//header1.h
#include <iostream>
#include<string>
#include <cmath>
using namespace std;
namespace ns1 {class Student //聲明Student類{public:Student(int n, string nam, int a){num = n;name = nam;age = a;}void get_data();private:int num;string name;int age;};//成員函數的定義void Student::get_data(){cout << num << "" << name << "" << age << endl;}//定義全局函數(即外部函數)double fun(double a, double b){return sqrt(a + b);}
}
在header2.h中,聲明命名空間ns2,并在命名空間ns2中定義Student類和 成員函數以及fun函數。
using namespace std;
namespace ns2
{class Student{public:Student(int n, string nam, char s)//參數與header1.h中不同{num = n;name = nam;sex = s;}void get_data();private:int num;string name;char sex;};//成員函數定義void Student::get_data(){cout << num << "" << name << "" << sex << endl;}//定義全局函數double fun(double a, double b){return sqrt(a - b);//返回值與header1中的fun函數不同}
}
主函數如下:
#include "header1.h";
#include "header2.h";
int main()
{ns1::Student stud1(101, "Wang", 18);//用命名空間ns1中的Student類定義stud1stud1.get_data();cout <<ns1:: fun(5, 3) << endl;//調用命名空間ns1中的fun函數ns2::Student stud2(102, "Li", 'f');//用命名空間ns2中的Student類定義stud2stud2.get_data();cout << ns2::fun(5, 3) << endl;//調用命名空間ns2中的fun函數return 0;
}
運行結果如下: