Appearance
命名空间
一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,不可避免地会出现变量或函数的命名冲突。当所有人的代码都测试通过,没有问题时,将它们结合到一起就有可能会出现命名冲突。
例如小李和小韩都参与了一个文件管理系统的开发,它们都定义了一个全局变量fp
,用来指明当前打开的文件,将他们的代码整合在一起编译时,很明显编译器会提示fp
重复定义(Redefinition
)错误。
使用简例
为了解决合作开发时的命名冲突问题,C++引入了命名空间(Namespace
)的概念。请看下面的例子:
cpp
namespace Li {
//小李的变量定义
FILE* fp = NULL;
}
namespace Han {
//小韩的变量定义
FILE* fp = NULL;
}
TIP
命名空间有时也被称为名字空间、名称空间
小李与小韩各自定义了以自己姓氏为名的命名空间,此时再将他们的fp
变量放在一起编译就不会有任何问题。
命名空间
namespace
是 C++ 中的关键字,用来定义一个命名空间,语法格式为:
cpp
namespace name {
//variables, functions, classes
}
name
是命名空间的名字,它里面可以包含变量、函数、类、typedef
、#define
等,最后由大括号对{ }
包围。
变量和命名空间
当你在命名空间里面放了变量的时候就要注意了,有以下几种情况
变量没有初始化
在下面这种情况下,你对其直接使用的话,它就是一个全局变量,全局变量会自己做初始化,比如下面这段代码,int
类型的值会自动初始化变成 0
cpp
#include<iostream>
namespace water {
int count;
double a;
}
int main() {
std::cout << water::a << std::endl;
std::cout << water::count << std::endl;
}
运行结果如下
txt
0
0
TIP
注意此处的代码,我们并没有使用语句using namespace std;
,所以使用cout
和endl
的时候就要专门声明std::
,相应的,water
命名空间也是一样
命名空间变量已初始化
变量初始化之后就可以开始使用变量了,所以在编译的时存在变量重名,能正常通过。
但是编译过了不代表你能精确使用重名的变量,当变量存在重名的时候,就需要使用一些指定使用变量的方法,下面我们先看一个例子
cpp
#include<iostream>
namespace water {
int count = 1;
double a;
}
int main() {
// 此处的count是局部变量
int count = 9;
std::cout << water::count << std::endl << water::a << std::endl;
}
请看仔细些,我们要把上面这段代码☝和下面的代码👇做比较
cpp
#include<iostream>
namespace water {
int count = 1;
double a;
}
int main() {
// 这段代码会直接报错,因为 count 既不是局部变量也是不是 water 命名空间内部的变量
// 那就是全局变量,但是没有对 count 进行声明,于是报错
count = 9;
std::cout << water::count << std::endl << water::a << std::endl;
}
请仔细查看上面代码以及注释,为了挽救第二段代码的情况,我们就要使用一些语句,让重名变量的使用都变得稍微明确些,请看下一小节
可行的修改示范
域解析操作符
使用变量、函数时要指明它们所在的命名空间。
以上面的fp
变量为例,可以这样来使用:
cpp
//使用小李定义的变量 fp
Li::fp = fopen("one.txt", "r");
//使用小韩定义的变量 fp
Han::fp = fopen("two.txt", "rb+");
::
是一个新符号,称为域解析操作符,在 C++ 中用来指明要使用的命名空间。
using关键字
除了直接使用域解析操作符,还可以采用using
关键字声明,例如:
cpp
// 使用小李定义的变量 fp
using Li::fp;
fp = fopen("one.txt", "r");
//使用小韩定义的变量 fp
Han :: fp = fopen("two.txt", "rb+");
在代码的开头用using
声明了Li::fp
,它的意思是,using
声明以后的程序中如果出现了未指明命名空间的fp
,就使用 Li::fp
但是如果我们要使用小韩定义的 fp
,就需要 Han::fp
。
using
声明不仅可以针对命名空间中的一个变量,也可以用于声明整个命名空间,例如
cpp
using namespace Li;
//使用小李定义的变量 fp
fp = fopen("one.txt", "r");
//使用小韩定义的变量 fp
Han::fp = fopen("two.txt", "rb+");
如果命名空间Li
中还定义了其他的变量,那么同样具有fp
变量的效果。
在using
声明后,如果有未具体指定命名空间的变量产生了命名冲突,那么默认采用命名空间Li
中的变量。
命名空间内部不仅可以声明或定义变量,对于其它能在命名空间以外声明或定义的名称,同样也都能在命名空间内部进行声明或定义,例如类、函数、typedef
、#define
等都可以出现在命名空间中。
站在编译和链接的角度,代码中出现的变量名、函数名、类名等都是一种符号(Symbol
)。有的符号可以指代一个内存位置,例如变量名、函数名;有的符号仅仅是一个新的名称,例如typedef
定义的类型别名。
下面来看一个命名空间完整示例代码
cpp
#include <stdio.h>
//将类定义在命名空间中
namespace Diy {
class Student {
public:
const char* name;
int age;
float score;
public:
void say() {
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
}
int main() {
Diy::Student stu1;
stu1.name = "小明";
stu1.age = 15;
stu1.score = 92.5f;
stu1.say();
}
运行结果是
txt
小明的年龄是 15,成绩是 92.500000
不被建议的修改示范
请观察下面这段代码,我们可以发现我们直接在main函数内部声明使用了water
的namespace
,这种大范围声明命名空间的行为不是一个好行为,请避免这么做
cpp
#include<iostream>
namespace water {
int count = 1;
double a;
}
int main(){
using namespace water;
// 这里是 water 命名空间里面的count
// 不建议这样做,因为频繁的切换语句运行时所在的命名空间很容易造成混淆
count = 9;
int count = 7;//这里因为是局部变量,所以也没有问题
std::cout << water::count << std::endl << water::a << std::endl;
}
我们查看运行结果
txt
9
0
DANGER
这绝不是一个好的编程习惯,频繁切换语句运行的默认命名空间是一件很危险的事情,搞不好就会出现一些神奇并且难以排查的bug