【转载】c++命名空间之谜
命名空间的由来
命名空间(namespace),是c++引入的一种机制,namespace目的是用来解决全局变量名与函数名或函数名与函数名之间名称相同的冲突的。c语言没有namespace,从c扩展而来的c++早期也没有,后来的c++、java、python都使用了这种技术,甚至在一些高级语言中把namespace设计得更好。
一个客观的事实就是一个庞大的项目,如linux内核,内部上万个.c文件,变量与函数名称重名是不能避免的,如同在中国,重名太正常不过了。本节我们就是要弄清楚在没有namespace情况下,c是怎么解决这个问题的,基于什么样的情况又发明了namespace,同时学习namespace的定义和引用方法,体会namespace这种解决问题的思路和方法。
c解决重名方法
c语言中我们解决命名有几个方面,首先单个文件内的重名由程序员自己解决,文件之间的或模块之间,如果是局部使用的函数使用static来限制作用域在文件内,必须外部访问的全局变量名前添加模块或团队的前缀,如SD卡的程序的全局以SD开头,usb的以usb开头。这些方法虽然能解决这些问题,其实和语言本身没什么关系,都是利用的一种行业潜规则来道德约定的,问题是很多人他并不会去遵守,所以这个解决的效果是有限的。
c++解决重名
c++解决重名问题设计了namespace命名空间语法,定义格式为namespacexx {},namespace是关键字,xx是自定义的空间名称,大括号是范围限定,也就是括号内是一个整体空间,可以有任何东西,如变量、函数等,括号内可以直接引用,而括号外的想相互访问必须指定空间名称+内部名称,namespace看起来就像是一种前缀。namespace本质上就是改变全局变量或函数的链接属性,即改变作用域。
语言特性的讨论
语言的特性,来源于实际需求,先有需求后才会有对应的语法特性,所以每一种语言的特性必定对程序猿有帮助。理论上说语言特性越多或越复杂,语言就越厉害,同时学习也更难。世界上没有简单、厉害、高效的语言,就像没有轻松赚钱的这种事。
语言的语法本质上是靠编译工具链支持的,说白了我们所使用的语法其实是在调用编译器内的功能,所以高级语言背后真正强大的是编译工具链。语言的变迁无非就是新增或修正语言特性,语言学习的重难点其实就在于语言特性的掌握和应用。解决实际问题最终还是
namespace基本语法
定义
用namespace关键字来定义一个命名空间,后紧跟空间名,大括号是本空间的代码块。空间内全局变量、函数资源共享。
例如:
namespace test
{
void func1(void );
void func2(void);
}
引用方法1
使用两个冒号“::”标识符来告诉编译器访问的是某命名空间内的某元素。和结构体中的点“.”或指针的箭头 ->作用类似,都是访问一个空间中的具体元素。
test :: func1();
test :: func2()
引用方法2
用using关键字对空间内的元素进行声明,这种声明在代码块内有效。using关键字有点类似于include关键字,需要注意的是using在声明空间内函数的时候不能带有括号(),只需要函数名即可。但是在实际调用该函数时还是需要括号。该引用方法实际上并不常用。
using test :: func1;//单独声明命名空间中的func1函数
func1(); //直接使用func1,无须添加其他的前缀
引用方法3
使用using来直接声明整个命名空间,就相当把命名空间内的所有成员都与当前代码块内共享。
using namespace test//将整个命名空间全部声明
func1(); //访问空间内的函数1
func2(); //访问空间内的函数2
跨文件引用
我们在一个文件定义了一个命名空间,在另外一个文件去引用,按c语言的套路应该是要在使用前去声明空间。不同的是声明的时候不能加extern。同时在声明的时候还要紧跟大括号,并在括号内声明内部的元素如函数和变量等。同时括号内的声明与c一样,函数不能带有实体,变量加extern。值得注意的是需要命名空间的夸文件引用涉及2步声明,第一步是命名空间的本体声明,目的是告诉编译器有这么一个命名空间存在,第二步声明是引用的一种手段,第二步声明没有的时候我们照样可以通过::来访问命名空间,当我们不想使用该符号去引用,就可以使用using来声明。
namespacetest //声明
{
void func1(void);
void func1(void);
};
hudz::func1();//引用方法1
using namespace test //引用声明
func1(); //引用
默认命名空间
默认命名空间又叫全局命名空间。典型的默认命名空间就是main函数,函数或变量没有放在其他的命名空间中,编译器会把它们归类到一起组成一个命名空间。假如我们的程序一个namespace都没有定义,那么所有的变量和函数都在一个空间内。就是我们c中的普通编程。
默认命名空间引用
要在一个命名空间内访问,默认命名空间的元素,由于默认命名空间没有名字,所以我们之前的那一套通过名字来引用内部元素显然不适用。编译器给了一个更简单的方法,就是粗暴的省略前面的空间名,直接双冒号开始访问元素名即可实现,而更为优化的方案是直接使用,不加::也可以,只是可读性差一点,但是我们还是推荐写上::。当然命名空间只是一个空间,并不是函数,空间内的函数是否会被执行,主要看是否被引用。
::func();
匿名命名空间
从字面意思“匿名”就是没有名字,主要作用是用来对限定本文件作用域的,如我们c语言中用static来限制函数只在本文件有效。但是static不能用来限制如struct、enum等类型的作用域,他们前面添加static编译器就会报错。但是匿名空间却可以,匿名空间内啥东西都可以有。所以匿名空间的存在也就是为了解决c中static不能解决的问题。默认命名空间是编译器为我们分类的一个全局匿名空间,我们自己定义的匿名空间,在引用上与默认命名空间相同。
定义
定义匿名空间与常规的命名空间相似,就是少了命名空间的名称。因为没有名称,所以在基于靠名称访问空间内的元素的这么一个语言下,根本就无法按照常规命名空间的进行声明和定义,自然在其他文件中不可能进使用。
namespace
{
void func(void)
{
void func1(void)
}
}
引用
匿名命名空间的引用,在本文件内与默认命名空间相同,同样都遵守命名空间的基本引用语法,匿名命名空间就是用来限制夸文件访问的,所以夸文件啥都不好使。由于文件内的引用与c中无命名空间的访问相同,就不在详述。
命名空间嵌套
命名空间的嵌套指的是命名空间中又包含了命名空间,他的声明仅是多一组::符号,引用也仍然符合基本命名空间的规则,当命名空间内嵌套的命名空间内的函数与上层命名空间的函数或变量名重名,编译器会优先使用作用域小的那个,和c中的全局变量与局部变量重名时,会使用局部变量一个道理。
namespace总结
namespace是用来解决名称冲突的。同时提供命名空间之间的相互访问机制。
使用namespace test{int i ;}来定义,可直接使用test::i=1方式来访问,也可使用using namespace test来声明后i=1来访问。也可以using test1::i定义,i=1来访问。
特殊的命名空间有匿名命名空间和默认匿名空间,匿名命名空间是用来将空间内所有成员的作用域限定在当前文件内的,默认命名空间就是我们没有指定命名空间的元素,编译器统一安排到一个命名空间,这个命名空间是全局属性。
学习c++不能全靠记忆,需要理解,带着要解决问题的目的,去学习c++是如何解决的问题。站在编译器设计者的立场去体会,为什么要这么弄。如果是我我有没有更好的办法
论坛好像不能发送链接,没办法标注原作者...... sun_chuan 发表于 2023-8-4 21:27
论坛好像不能发送链接,没办法标注原作者......
如果你要发把https://去掉让他变成普通文本
页:
[1]