天天看點

02~C++名字空間

文章目錄

    • 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的原理…有興趣的話可以翻看一本書《程式員的自我修養》

繼續閱讀