名字,具备属性:'使用范围',代表名字能够在那些地方使用函数
实体,具备属性:'生存期',描述着实体什么时候被建立,什么时候被销毁;就像一个实实在在存在的物理实体..工具
程序中函数,变量,他们均可以看做实体spa
而模板函数,模板类,这些不该该看做实体,.由于他们只是一个概念,蓝图,公式...指针
经过定义/声明语句,能够将实体与名字关联起来,如:code
int i=33; /* 经过定义语句,将名字i与int实体关联起来 */ extern int i; /* 经过声明语句,将名字i与int实体关联起来 */
定义了名字.使用范围,与实体.生存期,做用域分为局部做用域,全局做用域;orm
若在局部/全局做用域中声明/定义一个名字,则名字.使用范围为被声明/定义的位置,一直到做用域的结束;对象
若在局部做用域中定义一个实体,则该实体.生存期为[被定义的位置,做用域的结束];继承
若在全局做用域中定义一个实体,则该实体.生存期为[程序开始运行,程序运行结束]; 作用域
查找规则定义了当遇到一个名字时,如何肯定它所关联的实体.编译器
经过外围做用域由内向外查找,外围做用域多是一个或多个嵌套的命名空间;
只考虑在使用点以前定义/声明的名字;
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 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 声明出如今类做用域时,'命名空间名'只能是该类的基类名之一,'成员名'也只能是该基类的成员之一.
当派生类继承基类时,由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 namespace 命名空间名.
做用域: 同using声明.
语法: namespace 命名空间1=命名空间2.命名空间2能够是嵌套的命名空间.