命名空間

名字/實體

  • 名字,具備屬性:'使用範圍',代表名字能夠在那些地方使用函數

  • 實體,具備屬性:'生存期',描述着實體什麼時候被建立,什麼時候被銷燬;就像一個實實在在存在的物理實體..工具

    • 程序中函數,變量,他們均可以看做實體spa

    • 而模板函數,模板類,這些不該該看做實體,.由於他們只是一個概念,藍圖,公式...指針

定義/聲明

  • 經過定義/聲明語句,能夠將實體與名字關聯起來,如:code

int i=33;       /* 經過定義語句,將名字i與int實體關聯起來 */
extern int i;   /* 經過聲明語句,將名字i與int實體關聯起來 */

做用域

  • 定義了名字.使用範圍,與實體.生存期,做用域分爲局部做用域,全局做用域;orm

  • 若在局部/全局做用域中聲明/定義一個名字,則名字.使用範圍爲被聲明/定義的位置,一直到做用域的結束;對象

  • 若在局部做用域中定義一個實體,則該實體.生存期爲[被定義的位置,做用域的結束];繼承

  • 若在全局做用域中定義一個實體,則該實體.生存期爲[程序開始運行,程序運行結束];    作用域

C++查找規則

  • 查找規則定義了當遇到一個名字時,如何肯定它所關聯的實體.編譯器

若名字未被限定

  • 經過外圍做用域由內向外查找,外圍做用域多是一個或多個嵌套的命名空間;

  • 只考慮在使用點以前定義/聲明的名字;

namespace X{

int i=33;   /* A */
int test( void ){
    Println("%d",i);    
    /**
    * 對於名字i的查找順序:test函數造成的局部做用域->test函數所處的命名空間X->命名空間X所處的全局命名空間; 
    * 而且注意'只考慮在使用點以前定義的名字',因此這裏的名字i引用的是A處定義的int實體.
    */
    int i=77;/* B */
    Println("%d",i);/* 一樣這裏的名字i引用的是B處定義的實體 */
}

}

名字已被限定

  • 即此時名字已被類名,或者命名空間名經過做用域運算符::限定.此時僅在指定的類,命名空間中肯定名字關聯的實體.           

int main( int argc,char *argv[] ){
    X::test();  /* 名字test被命名空間X限定,則只在命名空間X中查找名字test對應的實體 */
}

特殊的例外

  • 當以類對象,或者類類型指針調用函數時,而且函數名未被命名空間或類名限定,則會在定義這些類及其基類的命名空間中查找函數的定義/聲明,從而肯定函數名所關聯的實體.

StartSpace(X)
class Base{};
void	test( const Base & ){ Println("HelloWorld"); }
EndSpace

StartSpace(Y)
class D:public X::Base{};
EndSpace

int main( int argc,char *argv[] ){
	Y::D d;
	test(d);
	/**
	 * 此時會在定義類D的命名空間Y,以及類D的基類Base所在的命名空間X中查找test的聲明/定義.
	 * 因此此時會查找到 X::test(const Base &);
	 */
}

  • 若是函數名已被命名空間或類名限定,則遵循'名字已被限定'規則.

友元的隱式聲明

  • 當在類中進行友元函數/類聲明時,若函數或類的聲明不可見,則friend語句具備將該函數/類的聲明放入外圍做用域(即:定義類的做用域/命名空間)的效果.如:

namespace A{
class C{
    friend void f( const C & );
};
}

void f2(){
    A::C obj;
    f(obj);
    /** 
     * 由於函數f接受類類型引用形參而且以類類型對象調用,因此會在定義C的命名空間中查找名字f. 
     * 又由於f經由friend隱式在命名空間A中聲明,因此這裏調用的是A::f();
     */
}

全局命名空間

  • 就像地址空間,是C++源程序中全部合法名字的集合.

  • 命名空間是一個工具,是用來劃分'全局命名空間'.這樣能夠有效的避免名字衝突(即一個名字與多個實體關聯).

  • 全局命名空間也是一個命名空間!定義在全局做用域中的名字是定義在全局命名空間的,此時經過'::名字'訪問.

命名空間

  • 與做用域的關係: 命名空間與做用域之間沒有任何聯繫.命名空間只是一個工具.

namespace A{
	int i=33;
	/**
	 * 此時名字i的使用範圍,同在全局做用域中聲明i同樣,只不過在A以外訪問名字i須要添加限定符.
	 * i所關聯的實體也與在全局做用域中定義i同樣,生存期:[程序開始,程序結束]
	 */
}

  • 不連續性: 命名空間能夠是不連續的,如:

namespace XXX{
    聲明.
}
若命名空間XXX已經存在,則此時打開命名空間XXX,而後將'聲明'放入命名空間中.
若命名空間XXX還沒有存在,則此時建立一個新的命名空間.

  • 嵌套性: 一個命名空間能夠嵌套在另外一個命名空間中定義,

未命名的命名空間

  • 語法: namespace{ /* 未命名的命名空間 */ }

  • 語義: 定義只限當前文件訪問的成員.就像C中的static.

  • 不連續: 未命名的命名空間也是不連續的,只是不能夠跨越文件.

/* file1.cc */
namespace{ int i=33; }    /* 則i只限於file1中使用 */
/* file2.cc */
namespace{ int i=77 }    /* 在鏈接時不會由於file1.cc中的i而產生重定義. */

  • 訪問: 未命名命名空間中定義的名字能夠在定義該未命名命名空間的命名空間中找到,即:

/* file1.cc */
namespace { int i=33 }    /* 未命名命名空間處在全局命名空間中,此時直接經過'i'來訪問該實體 */
namespace X{
namespace { int j=33 }    /* 未命名命名空間處在命名空間X中,此時直接經過'X::i'來訪問該實體 */
}

int k=77;
namespace { int k=77; }
Println("%d",k);/* 此時會形成二義性,由於沒法肯定k來自全局命名空間,仍是來自未命名的命名空間. */

  • 應該使用未命名命名空間來代替static.如:

#ifdef	__cplusplus
#	define 		_LocalLeft		namespace {
#	define		_LocalRight 	}
#else
#	define 		_LocalLeft 		static
#	define 		_LocalRight
#endif

/** 定義一個僅限於當前文件使用的成員 */
#define 	Local(var)	_LocalLeft var ; _LocalRight
/* 示例: Local(int i=33);則i不會被其餘文件引用. */

定義命名空間中的成員

  • 在命名空間內部定義,此時不須要使用限定符來限定函數名,如:

/* file1.h */
namespace X{
void f();
};
/* file1.cc */
namespace X{
void f(){
    ;
}
};

  • 在命名空間外部定義.此時須要使用限定符,另外當編譯器看到被命名空間名(如:X)限定的函數名後,就代表函數已經處於命名空間X的做用域中了,此時若函數的參數表與函數體內使用了X中的其餘成員並不須要使用'X::'限定.

/* fiel1.h */
namespace X{ 
class C{};
C f(const C &); 
}
/* file1.cc */
X::C    X::f(const C &){    /* 此時返回類型仍須要使用'X::'限定符 */
    return C();
}

  • 只能在包含成員聲明的命名空間中定義成員.如上,包含f()函數聲明的命名空間有:命名空間X,全局命名空間.因此只能在X與全局命名空間中定義函數f().

using聲明

  • 語法: using 命名空間名::名字,示例: using std::endl;

  • 語義: 對於指定的名字搜索,添加一個搜索範圍,如:

namespace A{
	int i=33;
}

//using A::i;

int main( int argc,char *argv[] ){
	Println("%d",i);
	/**
	 * 此時根據查找規則,i的搜尋範圍:main造成的局部做用域->main所在的全局命名空間.
	 * 因此此時提示沒法肯定i所關聯的實體.
	 * 在使用 using A::i 以後,此時對i的搜尋範圍是:
	 *     main造成的局部做用域->main所在的全局命名空間->命名空間A.
	 * 因此此時能夠肯定i所關聯的實體,即在命名空間A中定義的int實體
	 */
}

  • 做用域: 出如今全局做用域/局部做用域中時,此時using聲明的有效範圍:[using聲明所處位置,做用域結束]

出如今類中的using聲明

  • 當using 聲明出如今類做用域時,'命名空間名'只能是該類的基類名之一,'成員名'也只能是該基類的成員之一.

  • 當派生類繼承基類時,由C++對象模型可知,派生類會繼承基類的全部數據成員.對於基類的成員函數,派生類的繼承狀況能夠總結爲'對於基類的成員函數,若派生類中不存在同名函數則會被繼承,不然不會被繼承(此時須要using聲明顯式繼承).'

class Base{
public:
	void print(){ Println("Hello"); }
};

class D:public Base{
public:
	using Base::print;
	void print(int){ Println("D"); }
};
/**
 * 由於D中已經存在print(int),因此D不會繼承Base::print()函數.
 * 除非使用了using Base::print,如上
 */

using指示

  • 語法: using namespace 命名空間名.

  • 做用域: 同using聲明.

命名空間別名

  • 語法: namespace 命名空間1=命名空間2.命名空間2能夠是嵌套的命名空間.

相關文章
相關標籤/搜索