命名空间
简介
- 命名空间是由用户定义命名的作用域空间,常用来处理同名冲突以及其它问题,例如:
- 某个代码文件中定义了一个函数,可能在别的文件中有同名的函数,链接时就会出错,可以加上static来限制,但是如果别的文件中需要使用到函数就不好控制了。
- 为了避免命名冲突,实现一个库时一般会给函数名手动加上库名,因为担心别的文件中也定义了同样的函数名,如果使用命名空间就方便很多。
实现原理
- 类似于函数重载,编译时编译器会自动给命名空间中的函数和变量加上命名空间名处理后的特殊字符。
- 验证
namespace a{
void test(){
std::cout << "test" << std::endl;
}
}
xxx:~/workspace/cxx$ g++ -c test.cpp
xxx:~/workspace/cxx$ nm test.o
.....
0000000000000000 T _ZN1a4testEv
....
* 将namespace a改为namespace b,重新查看
xxx:~/workspace/cxx$ nm test.o
.....
0000000000000000 T _ZN1b4testEv
....
定义
- 普通方式,例如:
namespace A{
xxx;
}
- 嵌套方式,例如:
namespace A{
namespace B{
xxx;
}
}
- 在多个代码文件中可以存在多个同名命名空间,同名命名空间就是同一个命名空间。
使用
- 不声明使用哪个命名空间, 使用命名空间名和作用域标识符进行访问, 例如:
* 命名空间名::成员名
* std::cout << "xxxx" << std::endl;
- 声明使用某个命名空间, 可以直接使用该命名空间名中的成员,例如:
* using namespace 命名空间名;
* 例子:
using namespace std;
cout << "xxxx" << endl;
- 声明使用某个命名空间中的某个成员, 可以直接使用该命名空间名中的成员,例如:
* using 命名空间名::成员名;
* 例子:
using std::cout;
using std::end;
cout << "xxxx" << endl;
- 方式2和方式3的区别:方式2会把namespace中定义的所有函数和变量都导入,如果 namespace中定义了class a,你在代码中又定义class a,就会报错;
而方式3只是将某个成员导入,不会污染整个作用域。 - 优缺点:
- 方式1编码效率较低,但是安全,不会出问题。
- 方式3编码效率最高,但是可能会出问题。
- 方式2兼顾编码效率和安全。
注意点
- 可以多次使用"using namespace xxxx",但是使用多个容易导致命名冲突。
- 声明了使用某个命名空间,暂时不清楚取消的办法。
特殊命名空间
- 每个类都是一个独立的命名空间,通过查看编译生成的中间文件的符号表说明,类的每个成员函数在符号表中的名字都会加上类名,如下:
#include <iostream>
class base{
public:
void test(){
xxxxx;
}
};
* 符号表中的函数名
0000000000000000 W _ZN4base7testEv
作用域标识符
说明
- ::被称为域解析符(也称作用域运算符或作用域限定符),用来连接类名和函数名,指明函数属于哪个类。
使用
普通命名空间或类
void Student::say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
全局成员或无类名
//无类名
::say(){
xxxx;
}
//无类名也无域解析符
say(){
xxxx;
}
- 当局部变量和全局变量名一样时,在c语言中全局变量会被屏蔽,不建议这样命名,但是在c++中,在局部可以如下访问全局变量。
::a = 1;