C++命名空間超級大詳解

1 什麼是命名空間?

  本質上,命名空間就是一個範圍標識,比如大家都在中國,有中國湖北、中國北京、中國上海、中國香港、中國澳門、中國臺灣,其中中國在程序中就是全局,湖北、北京、上海、香港、澳門、臺灣,這些城市就是一個個命名空間 。

2 爲什麼要有命名空間

  之所以設計命名空間是爲了解決命名衝突的問題,比如獅子山,全國有很多個獅子山,比如湖北獅子山、香港獅子山等等,那麼怎樣正確地標識這些重名的地方呢?答案就是使用命名空間,例如,湖北::獅子山,香港::獅子山。如果大家學習了操作系統,就會發現這和文件系統中多級目錄的作用十分相似,都是爲了解決命名衝突問題。

大型程序往往會使用多個獨立開發的庫,這些庫又會定義大量的全局名字,如類、函數和模板等。當應用程序用到多個供應商提供的庫時,不可避免地會發生某些名字相互衝突的情況。多個庫將名字放置在全局命名空間中將引發命名空間污染(namespace pollution)
命名空間(namespace) 爲防止名字衝突提供了更加可控的機制。命名空間分割了全局命名空間,其中每個命名空間都是一個作用域。通過在某個命名空間中定義庫的名字,庫的作者(以及用戶)可以避免全局名字固有的限制。 --摘自《C++ Prime中文版(第五版)》

  大體上,一個C++程序在內存上可以分爲堆、棧、全局區、只讀常量區、代碼區。

  1. 堆區,由程序員自己使用new delete之類的內存分配函數自己分配,自己釋放。
  2. 棧區,由系統進行內存管理,主要存放函數的參數以及局部變量。
  3. 全局區,主要存放全局變量、靜態變量,生命週期爲程序的整個運行期間,作用範圍是整個程序。
  4. 只讀常量區,是用來存放常量的,只可以讀取,不可修改。
  5. 代碼區,存放程序體的二進制代碼,只讀

注:這只是大體的分區

命名空間可以被嵌套定義,但是隻能定義在全局區,不能在堆、棧等其他區。

3 命名空間裏有些什麼

  1. 變量
  2. 函數
  3. 模板

頭文件

#ifndef _NAMESPACE1_H
#define _NAMESPACE1_H
namespace QZQ1 {
	extern int age;
	extern  char* name;
	void QzqPrint();
	template<typename T>void swap(T& a, T& b) {
		T c = a;
		a = b;
		b = c;
	}
	class A {
		public:
		int a;
		int b;
	};
};
#endif 

cpp文件

#include "namespace1.h"
#include <iostream>

namespace QZQ1 {
   int age;
   char* name;
   void QzqPrint() {
   	std::cout << "hello,word" << std::endl;
   }
};

這一塊我找了很久,沒有找到明確的說明,但是上述皆可在命名空間中定義。

4 怎樣創建一個命名空間

1. 創建方法

namespace  命名空間名字{
	//空間內容
};

例如:

namespace pig{
 	int flag;
	struct Information* GetInformation();
	void KillPig(int num);
};

注:注意區分命名空間和結構體的區別,別用混了。

2. 頭文件和cpp文件

  在命名空間中,必須把聲明寫在頭文件中,大部分情況下必須把定義寫在cpp文件中,否則當頭文件被多次包含後就會出現重定義的錯誤。遇到定義和聲明無法分離的東西,比如模板,就直接寫在頭文件中。

頭文件

#ifndef _NAMESPACE1_H
#define _NAMESPACE1_H
namespace QZQ1 {
	extern int age;
	extern  char* name;
	void QzqPrint();
	template<typename T>void swap(T& a, T& b) {
		T c = a;
		a = b;
		b = c;
	}
};
#endif 

cpp文件

#include "namespace1.h"
#include <iostream>

namespace QZQ1 {
   int age;
   char* name;
   void QzqPrint() {
   	std::cout << "hello,word" << std::endl;
   }
};

3.命名空間的定義可以不連續

命名空間可以定義在幾個不同的部分
頭文件

#ifndef _NAMESPACE1_H
#define _NAMESPACE1_H
namespace QZQ1 {
	extern int p1;
	extern int p2;
};
#endif 

cpp文件

#include "namespace1.h"
#include <iostream>

namespace QZQ1 {
	int p1;
};

namespace QZQ1 {
	int p2;
};

4. 命名空間可以嵌套定義

namespace A {
	namespace B1 {
		int a;
	};
	namespace B2 {
		int a;
	};
	int a;
};

5. 未命名的命名空間

namespace {
	int a;
	int b;
};

未命名的命名空間是爲了替代之前C語言的靜態變量而出現的,以前C語言用靜態變量,現在在C++中,使用未命名的命名空間來替代即可。未命名的命名空間僅在本文件中有效,每一個文件只有一個未命名的命名空間,處於頂層的未命名的命名空間中的變量不可以和外部的全局變量同名,如下所示就是錯誤的:

int a;
namespace {
	int a;
};

未命名的命名空間也是可以嵌套定義的,如下所示

namespace {
	namespace {
		int a;
	}
};

這種嵌套只是方便邏輯上地規劃,並沒有真正地物理上嵌套,要注意地是,未命名嵌套中非頂層的命名空間中的元素依舊不能和外部的變量重名,如下所示是錯誤的:

int a;
namespace {
	namespace {
		int a;
	}
};

5 怎樣使用命名空間

1. 通過命名空間名使用

namespace pig{
   int flag;
   int GetAge(int num) {
   	return 2 * num;
   }
};
int main() {
   pig::GetAge(10);
   return 0;
}

2. 通過using方法使用

使用using有兩種用法,一種是直接using namespace::SpaceName,稱爲using指示,之後就可以直接使用命名空間內的成員了,如下所示:

using namespace std;
using namespace pig;
int main() {
   flag = 1;
   cout << flag << " " << GetAge(10) << endl;
   return 0;
}

第二中using用法是using SpaceName::成員,稱爲using聲明,之後就可以直接使用對應的成員了,不過其他的成員依舊需要加命名空間名字做前綴,如下所示:

using  std::cout;
using pig::flag;
int main() {
	flag = 1;
	cout << flag << " " << pig::GetAge(10) << std::endl;
	return 0;
}

需要好好理解using指示和using聲明,要慎用using指示,應爲using指示比較容易造成命名衝突

3. 別名

namespace me = pig;

別名的作用就是用較短的名字來替代較長的名字,並沒有特別的含義。別名與原來的命名空間等價。

4. 注意事項

  • 不要在頭文件中使用using namespace SpaceName,即using指示,這樣操作的話,如果頭文件被多次包含,則命名衝突可能性會大大提高。頭文件中,可以選擇使用using聲明。
發佈了34 篇原創文章 · 獲贊 47 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章