类作用域与命名空间续-----留有后续疑问
2013-09-09 16:54
369 查看
类作用域与命名空间续
(名字查找规则)
在<C++primer>版本4中,有关于类作用域中的名字查找有这么一段话:
迄今为止,在我们所编写的程序中,名字查找(寻找与给定的名字相匹配的声明的过程)是相对直接的。
(1) 首先,在使用该名字的块中查找名字声明,只需考虑在该项使用之前声明的名字。
(2) 如果找不到该名字,则在包围的作用域(外层作用域,也需要在使用之前)中查找。
(3) C++中所有名字必须在使用之前声明。
(4) 在名字查找期间,如果类作用域使用的名字不能确定为成员名,则在包含该类或者成员定义的作用域中查找,以便找到该名字声明。
知道理论不见得理解了本质,或者说理解可能会有偏差。下面我们一如既往地做个小测试,测试程序如下:
#include"findName"
很容易明白,就是把findName文件的内容直接拷贝放在当前位置,那么在一个文件中,我们很容易理解show,Show无法调用的缘故,我们不能在声明之前使用名字。
如果我们非得把程序调通,也不是没有办法,解决方案有两种方案:
方案一:
方案二:
上述作用域风格其实是C++的全局作用域,我们可以使用C++的命名空间。
命名空间的名字查找规则:
(1) 类成员函数优先(按照子类--->基类顺序),一经找到停止。
(2) 若没有,在相应的命名空间中找(如using namespace std;就在std中查找)。
(3) 若还没有,就根据参数所在命名空间进行查找。
函数调用匹配原则:(自己理解的)
(1)首先匹配函数名,若能找到进行下一步匹配。
(2)接着匹配函数类型,若无法实现匹配(如参数是泛型),等实例化时再匹配。
(3)实例化时需要严格匹配。
也即一个模板可以在一个函数出现之前调用它,不过调用时的参数必须要有泛型参数,且实例化该模板时,被调函数必须在之前出现在相关命名空间。
我们先看一个简单的例子:
由此测试小程序,我们可以得出:
(1) Show(s)调用成功的原因,是由于模板函数的实例化时才算是定义
处,而实例化必须在调用时发生。且调用函数Show时,参数是泛
型参数,所以也须在调用时才能决定函数是否已经定义,而调用之前Show函数已经出现故调用成功。
(2) Show() 调用出错是因为,该处函数没有泛型参数。所以编译器可以直接作抉择,那么它会在在模板函数实例化之前匹配函数。此时Show()并没有定义,所以调用失败。
(3) Show(std::string("empty"))调用失败同(2)。
(4) 注意一下CallShow<int>()的调用方式。
当把下面代码置于namespacestd中的话:
调用仍然成功,这个不太符合我们的理解,因为在调用Show时,我们并没有在调用Show时注明名字域std::Show,这貌似不合理。其实这是名字查找的第三条规则:根据参数的命名空间进行查找。
在mian函数中写两条语句:
Show();
Show(std::string("test"));
不出所料,第一句编译错误:找不到函数名。第二句执行成功。
当把std 改为stdl时,两句话都编译不通过。
当上述Show不置于namespacestd中,而CallShow,display置于namespacestd中时。
编译不通过,错误很明显使用CallShow,display时必须加上作用域限定std::。
如果都加上namespacestd那么执行也会完全通过。
如果代码改为如下:
程序能实现正常功能。
但是如果把,下面Show的namespacestd去掉的话,那么就会出现编译错误。此时CallShow里只会匹配std中的那个Show,这是C++匹配规则。当在该命名空间中找到了就不会去外层作用域中查找。且不同命名空间之间不会发生重载机制。
本例中,Std中的Show不会与全局的Show形成重载,且当在std中找到Show之后就不会向外层查找(这其实是c语言中的名字屏蔽原则,局部屏蔽全局,可以用“::”显示调用)。然后发现参数不匹配所以出错。
但此处使用“::”不可以,原因不明。
如果去掉std中的Show定义就可以找到全局空间的Show正常运行。
一个小结论:命名空间可以直接使用全局空间中的名字,反之不可(必需用类似std::)。
我们再次改变代码:
这段代码能成功执行煞是费解,难道此处Show不是在实例化之后定义的吗? 这个问题留待以后学完模板吧。
(名字查找规则)
在<C++primer>版本4中,有关于类作用域中的名字查找有这么一段话:
迄今为止,在我们所编写的程序中,名字查找(寻找与给定的名字相匹配的声明的过程)是相对直接的。
(1) 首先,在使用该名字的块中查找名字声明,只需考虑在该项使用之前声明的名字。
(2) 如果找不到该名字,则在包围的作用域(外层作用域,也需要在使用之前)中查找。
(3) C++中所有名字必须在使用之前声明。
(4) 在名字查找期间,如果类作用域使用的名字不能确定为成员名,则在包含该类或者成员定义的作用域中查找,以便找到该名字声明。
知道理论不见得理解了本质,或者说理解可能会有偏差。下面我们一如既往地做个小测试,测试程序如下:
//findName 头文件 class client{ public: client(){} client(client& c){} ~client(){} void CallShow() { void show(); show(); //Show<int>(); 函数模板无法单独声明,而没有前项声 //明是不可以调用的 } private: int id; }; template<class T> class server{ public: server(){} server(server& s){} ~server(){} void CallShow() { void show(); show(); //Show<int>(); } }; |
//findName.cpp #include<iostream> #include"findName" using namespace std; void show() { cout<<"Wellcome to call me!"<<endl; } template<class T> void Show() { cout<<"0 == "<<static_cast<T>(0)<<endl; } int main() { client cus; cus.CallShow(); server<int> hos; hos.CallShow(); Show<int>(); //注意无参模板函数调用 Show<double>(); Show<char*>(); } |
很容易明白,就是把findName文件的内容直接拷贝放在当前位置,那么在一个文件中,我们很容易理解show,Show无法调用的缘故,我们不能在声明之前使用名字。
如果我们非得把程序调通,也不是没有办法,解决方案有两种方案:
方案一:
#include<iostream> void show() { std::cout<<"Wellcome to call me!"<<std::endl; } template<class T> void Show() { std::cout<<"0 == "<<static_cast<T>(0)<<std::endl; } #include"findName" using namespace std; int main() { client cus; cus.CallShow(); server<int> hos; hos.CallShow(); Show<int>(); Show<double>(); Show<char*>(); } |
#include<iostream> void show(); template<class T> //模板无前向声明方式 void Show() { std::cout<<"0 == "<<static_cast<T>(0)<<std::endl; } #include"findName" using namespace std; void show() { std::cout<<"Wellcome to call me!"<<std::endl; } int main() { client cus; cus.CallShow(); server<int> hos; hos.CallShow(); Show<int>(); Show<double>(); Show<char*>(); } |
命名空间的名字查找规则:
(1) 类成员函数优先(按照子类--->基类顺序),一经找到停止。
(2) 若没有,在相应的命名空间中找(如using namespace std;就在std中查找)。
(3) 若还没有,就根据参数所在命名空间进行查找。
函数调用匹配原则:(自己理解的)
(1)首先匹配函数名,若能找到进行下一步匹配。
(2)接着匹配函数类型,若无法实现匹配(如参数是泛型),等实例化时再匹配。
(3)实例化时需要严格匹配。
也即一个模板可以在一个函数出现之前调用它,不过调用时的参数必须要有泛型参数,且实例化该模板时,被调函数必须在之前出现在相关命名空间。
我们先看一个简单的例子:
#include<iostream> #include<string> template<class T> void CallShow() { //Show(); //Show(std::string("empty")); } template<class T> void CallShow(T s) { //show(); Show(s); //Show(std::string("empty")); } template<class T> class display{ public: void CallShow(T s) { Show(s); } }; void Show() { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } int main() { CallShow<int>(); std::string str = std::string("song"); CallShow(str); display<std::string> d; d.CallShow(str); } |
(1) Show(s)调用成功的原因,是由于模板函数的实例化时才算是定义
处,而实例化必须在调用时发生。且调用函数Show时,参数是泛
型参数,所以也须在调用时才能决定函数是否已经定义,而调用之前Show函数已经出现故调用成功。
(2) Show() 调用出错是因为,该处函数没有泛型参数。所以编译器可以直接作抉择,那么它会在在模板函数实例化之前匹配函数。此时Show()并没有定义,所以调用失败。
(3) Show(std::string("empty"))调用失败同(2)。
(4) 注意一下CallShow<int>()的调用方式。
当把下面代码置于namespacestd中的话:
void Show() { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } |
namespace std{ void Show() { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } } |
在mian函数中写两条语句:
Show();
Show(std::string("test"));
不出所料,第一句编译错误:找不到函数名。第二句执行成功。
当把std 改为stdl时,两句话都编译不通过。
当上述Show不置于namespacestd中,而CallShow,display置于namespacestd中时。
namespace std{ template<class T> void CallShow() { //Show(); //Show(std::string("empty")); } template<class T> void CallShow(T s) { //show(); Show(s); //Show(std::string("empty")); } template<class T> class display{ public: void CallShow(T s) { Show(s); } }; } |
如果都加上namespacestd那么执行也会完全通过。
如果代码改为如下:
#include<iostream> #include<string> namespace std{ void Show(int i){} template<class T> void CallShow() { //Show(); //Show(std::string("empty")); } template<class T> void CallShow(T s) { //show(); Show(s); //Show(std::string("empty")); } template<class T> class display{ public: void CallShow(T s) { Show(s); } }; } namespace std{ void Show() { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } } int main() { std::CallShow<int>(); std::string str = std::string("song"); std::CallShow(str); std::display<std::string> d; d.CallShow(str); //Show(); Show(std::string("test")); } |
但是如果把,下面Show的namespacestd去掉的话,那么就会出现编译错误。此时CallShow里只会匹配std中的那个Show,这是C++匹配规则。当在该命名空间中找到了就不会去外层作用域中查找。且不同命名空间之间不会发生重载机制。
本例中,Std中的Show不会与全局的Show形成重载,且当在std中找到Show之后就不会向外层查找(这其实是c语言中的名字屏蔽原则,局部屏蔽全局,可以用“::”显示调用)。然后发现参数不匹配所以出错。
但此处使用“::”不可以,原因不明。
如果去掉std中的Show定义就可以找到全局空间的Show正常运行。
一个小结论:命名空间可以直接使用全局空间中的名字,反之不可(必需用类似std::)。
我们再次改变代码:
#include<iostream> #include<string> namespace std{ void Show(int i){} template<class T> void CallShow() { //Show(); //Show(std::string("empty")); } template<class T> void CallShow(T s) { //show(); Show(s); //Show(std::string("empty")); } template<class T> class display{ public: void CallShow(T s) { Show(s); } }; } int main() { std::CallShow<int>(); std::string str = std::string("song"); std::CallShow(str); std::display<std::string> d; d.CallShow(str); } namespace std{ void Show() { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } } |
相关文章推荐
- 类作用域与命名空间续-----留有后续疑问
- 类作用域与命名空间续-----留有后续疑问
- 【转】C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型
- 变量的6种作用域:全局作用域,文件作用域,命名空间作用域,类作用域,局部作用域,语句作用域
- WCF 契约定义命名空间 的疑问
- [20140101_Example02]作用域、命名空间
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型 分类: 嵌入式开发学习 2014-11-28 10:42 233人阅读 评论(0) 收藏
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型(下)
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型
- 二 : using声明、using指示用于嵌套命名空间时的作用域
- c++中的作用域 四类(函数原型作用域,局部作用域,类作用域,命名空间作用域)
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型(上)
- C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型(上)
- C++学习小疑问:类的名称能否和命名空间的名称相同?
- 关于xml命名空间的——xmlns:f="http://www.name.com.cn/sub",一些疑问
- Python 变量作用域 —— 命名空间与 LEGB 规则