文章目錄
-
- 1.1 namespace 引入
- 1.2 namepace 調用方式優化
- 1.3 再探名字空間:名字隐藏
- 1.4 名字空間分類
- 1.5 namespace 實作機制
1.1 namespace 引入
廢話少說,看代碼,看結果
// namespace.cpp
#include <iostream>
namespace Department_X {
static int id = 99;
void fool(void)
{
std::cout << "departmentX" << std::endl;
}
}
namespace Department_Y {
static int id = 88;
void fool(void)
{
std::cout << "departmentY" << std::endl;
}
}
int main()
{
/*列印出部門X所有的id變量*/
std::cout << Department_X::id << std::endl;
/*使用部門X所有的fool函數*/
Department_X::fool();
/*列印出部門Y當中的id變量*/
std::cout << Department_Y::id << std::endl;
/*使用部門Y所有的fool函數*/
Department_Y::fool();
return 0;
}
輸出如下:
99
departmentX
88
departmentY
對于名字空間(英文名 namespace ),我們可以這樣了解:
名字空間A裡有自己的函數或變量
名字空間B裡有自己的函數或變量
名字空間A和B中的函數或者變量可以互相重名,不沖突
如果我們想要通路名字空間的資料的話,最直接的方法是指出名字空間,形如:
#名字空間std中的cout、endl
std::cout
std::endl
#名字空間Department_X中的變量id,函數fool
Department_X::id
Department_X::fool()
1.2 namepace 調用方式優化
喜歡便捷的讀者,可能會提出這樣一個疑問:
每次使用名字空間裡的變量或者函數,我都要不厭其煩的在前面寫一個 空間名字::函數名,假如我要通路數百個名字空間中的函數,我就要寫數百個名字空間名!!!多麼可怕
C++ 提供了另一種簡潔的方式,使用形式using namespace xxx的格式,将影響名字空間xxx中的函數或變量的可見性
// namespace.cpp
#include <iostream>
/*在檔案作用域中被引入可見性,後面所有的函數都不必調用類似std::cout,std::endl 啦*/
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
namespace Department_Y {
static int id = 88;
void fool(void)
{
cout << "departmentY" << endl;
}
}
/*函數bar1 引入Department_X的可見性*/
void bar1(void)
{
using namespace Department_X;
cout << "bar1 ";
/*自然是部門X中的那個fool()函數咯*/
fool();
}
/*在函數bar2 引入Department_Y的可見性*/
void bar2(void)
{
using namespace Department_Y;
cout << "bar2 ";
/*自然是部門Y中的那個fool()函數咯*/
fool();
}
int main()
{
bar1();
bar2();
return 0;
}
1.3 再探名字空間:名字隐藏
// namespace.cpp
#include <iostream>
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
void bar1(void)
{
using namespace Department_X;
cout << "bar1 ";
/*讀者會發現,此時的id是部門X中的那個*/
cout << "id = " << id << " &id = " << &id << endl;
/*此時又定義了一個新的id,它是一個棧位址,為什麼不再是部門X中的那一個了呢?
解釋就是using namespace xxx 隻做到将名字空間中的符号可見性給調用者,但是并不是把變量給生硬的引用在這裡*/
int id = 299;
cout << "id = " << id << " &id = "<< &id << endl;
}
int main()
{
bar1();
return 0;
}
為了了解我們還有下面的程式設計風格(變量引入到作用域,而非可見性)
// namespace.cpp
#include <iostream>
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
void bar1(void)
{
//這樣就是真的把部門X中的變量id引用弄到這個函數裡啦!!!
using Department_X::id;
cout << "bar1 ";
cout << "id = " << id << " &id = " << &id << endl;
}
int main()
{
bar1();
return 0;
}
那麼考慮下面的代碼,它其實不能編譯通過,為什麼呢?重複聲明/定義!!!現在,引入可見性和聲明的差別是否更加清晰了呢?
// namespace.cpp
#include <iostream>
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
void bar1(void)
{
using Department_X::id;
cout << "bar1 ";
cout << "id = " << id << " &id = " << &id << endl;
int id = 299;
//cout << "id = " << id << " &id = "<< &id << endl;
}
int main()
{
bar1();
return 0;
}
1.4 名字空間分類
C++文法規定:
全局或者全局類型統一位于匿名空間
使用者通過 namespace 定義有名空間的變量/類型
參考如下代碼
#include <iostream>
using namespace std;
namespace SpecName
{
void foo()
{
cout << "User Namespace::foo" << endl;
}
};
void foo()
{
cout << "Ni Ming namespace" << endl;
}
int main()
{
//using namespace SpecName;
SpecName::foo();// 有名空間的函數通路
::foo(); //全局/匿名空間的通路
return 0;
}
1.5 namespace 實作機制
我将嘗試說明namespace 在C++語言層面的實作機制
// file1 namespace_02.h
#ifndef __NAMESPACE_02__H
#define __NAMESPACE_02__H
namespace Department_X {
extern int id;
void fool(void);
};
namespace Department_Y {
extern int id;
void fool(void);
};
#endif // __NAMESPACE_02__H
/****************************************/
//file2 namespace_02.cpp
#include <iostream>
namespace Department_X {
int id = 99;
void fool(void)
{
std::cout << "departmentX id = " << id << std::endl;
}
}
namespace Department_Y {
int id = 88;
void fool(void)
{
std::cout << "departmentY id =" << id << std::endl;
}
}
/***************************/
//file3 main.cpp
#include <iostream>
#include "namespace_02.h"
void show_dep1(void)
{
using namespace Department_X;
fool();
std::cout << id;
}
void show_dep2(void)
{
using namespace Department_Y;
fool();
std::cout <<id;
}
int main()
{
show_dep1();
show_dep2();
return 0;
}
将上面的三個檔案一起編譯 連接配接生成可執行檔案 a.out
在ubuntu 系統中,使用GNU提供的工具集
nm a.out
你會看到符号表有這幾個東西
0000000000601068 D _ZN12Department_X2idE
000000000040093a T _ZN12Department_X4foolEv
000000000060106c D _ZN12Department_Y2idE
0000000000400977 T _ZN12Department_Y4foolEv
名字空間參與了符号修飾,如果讀者對編譯/連結 有所了解的話,很容易猜測出namespace的原理…有興趣的話可以翻看一本書《程式員的自我修養》