在编程中经常用到 using namespace std和 using std::cout,这两个语句似乎干的是一样的工作--减少要敲的字母,但仔细推敲下来,它们的语义差别还是挺大的。
#include<cstdio> namespace test { void output(){puts("namespace");} } void output(){puts("no namespace");} int main(int argc,char **argv) { output();//print "no namespace" using test::output; output();//print "namespace" //using namespace output; //编译错误,output重定义 //output(); return 0; }
由以上代码来看
1.using test::output 使test名字空间里的 output 覆盖了全局名字空间里的 output。
2.using namespace test 会产生重定义错误,说明没有发生覆盖,说明using namespace不等同于 对 test名字空间里的所有名字使用 using test::***。
再看以下代码:
#include<cstdio> namespace test { int x=3; } int x=5; int main(int argc,char **argv) { int x=6; printf("%d\n",x); //6 using test::x; //编译错误,重定义。 printf("%d\n",x); return 0; }
前两行说明符号表是有优先级的,会产生覆盖作用。 当前作用域的符号表的优先级高于全局作用域的符号表。using test::x产生了重定义的错误,由此可见,test::x和main里面定义的x在一个符号表里了。所以using test::x的作用就是将test::x加入当前作用域的符号表。
#include<cstdio> namespace test { int x=3; } int x=5; int main(int argc,char **argv) { int x=6; using namespace test; printf("%d\n",x); return 0; }
以上代码并没有产生重定义错误,输出结果是6。说明namespace引入的名字的优先级比当前作用域的要低。
#include<cstdio> namespace test { int x=3; } int x=5; int main(int argc,char **argv) { using namespace test; printf("%d\n",x); return 0; }
上面的代码又产生了重定义错误,说明using namespace引入的符号表的优先级与全局作用域里的符号表的优先级一样,都比当前作用域里的符号表的优先级低。
总结起来,就是 using的作用就是将名字引入当前作用域的符号表,而using namespace 相当于将namespace的所有名字释放到全局作用域中。
另外还能看到的是,编译器在查找名字时先查找当前作用域,再查找全局作用域。如果引入类,这相查找规则又发生了什么变化呢?
#include<cstdio> int x=3; class base { public: base():x(5){}; int x; }; class test :public base { public: void output() { int x=4; printf("%d",x); } test():x(1){} private: int x; };
通过将函数成员中声明的x与类里声明的、基类声明的、全局作用域声明的相互比较,得出结论:编译器先查找当前成员函数,再查找当前类作用域,然后查找基类作用域,最后查找全局作用域。高优先级作用域中的名字会覆盖低优先级中的名字。另在实验中也发现private声明不对查找规则产生影响。
所有名字查找都遵守以上规则,但函数名查找有其特殊性。首先,类的成员函数要通过对象来调用,这就限定了查找只能在该类和其基类中进行。其次,函数名查找有一种“实参相依”的查找规则:在查找函数时也要到其参数所在名称空间中去查找。这个规则用于非成员函数。
namespace Test{ class X{...}; void f(const X&); void g(X*); }; //... int g(Test::X *); void fun(){ Test::X a; f(a); g(&a);//错误,有歧义 a = a + a; };
补记:以上所说的其实是关于namespace的几条规则:1.当前作用域,全局作用域、类作用域都可看做名字空间。2.namespace是可嵌套的,也可说成是可分层的,即一个namespace里有别的namespace。3.最上层的名字空间是全局作用域,其它名字空间都是它的子名字空间。4.编译器在查找名字的顺序是先查找当前名字空间,然后依次查找父名字空间。5.using 将名字加入到当前名字空间。6.using namespace nm将nm合并到全局作用域。7.using 和using namespace的效用只限于当前作用域。8.未命名的名字空间会被合并到全局作用域。