概念
声明区域(declaration region):
- 所在文件 (函数外的全局变量)
- 所在代码块 (花括号内的声明)
潜在作用域(potential scope):从声明点开始,到声明区域的结尾。
作用域:变量对程序而言,可见的范围。
命名空间:避免相同名称的变量发生冲突。可以是全局,可以位于另一个名称空间中,但不能在代码块中。
全局命名空间:对应文件级的声明区域。
访问命名空间:::
作用域解析运算符,如:std::cout << "xxx" << endl;
。
未限定名称, 如:pail
;限定名称,如:jack::pail
。
using声明:using jack::pail;
只能直接使用jack中的pail。
using编译指令:using namespace jack;
可以使用jack中的全部名称。
示例
#include <iostream>
using namespace std;
namespace Jack {
int age = 12;
void greet();
}
void Jack::greet()
{
// 命名空间中的函数,可以默认的
// 使用同一命名空间中的其他名称
cout << "I'm " << age << endl;
}
int main(int argc, char *argv[])
{
// 其他函数中则需要以下格式:
// 空间名::变量名
Jack::greet();
cout << Jack::age << endl;
return 0;
}
两个要点:
- 命名空间中的函数,可以默认的使用同一空间中的其他名称
- 通过作用解析运算符
::
访问命名空间中的名称:Jack::pail
使用using
using声明
通过using声明,将名称添加到局部声明区域中:
#include <iostream>
using namespace std;
namespace Jack {
int age = 12;
void greet();
}
void Jack::greet()
{
cout << "I'm " << age << endl;
}
int main(int argc, char *argv[])
{
using Jack::age; // 针对变量
using Jack::greet; // 针对函数
cout << age << endl; // 使用变量
greet(); // 使用函数
return 0;
}
利用using声明, 不论是变量还是函数,都只需要使用名称。但每个using声明,只能使一个名称可用。
using编译指令
using编译指令能使所有名称可用:
#include <iostream>
using namespace std;
namespace Jack {
int age = 12;
void greet();
}
void Jack::greet()
{
cout << "I'm " << age << endl;
}
int main(int argc, char *argv[])
{
using namespace Jack; // using编译指令
cout << age << endl;
greet();
return 0;
}
比较
相同名称的处理(局部变量)
using声明不允许导入已经存在的相同的名称:
// 不允许声明方法
int main(int argc, char *argv[])
{
int age = 40; // 已经存在的age
using Jack::age; // 再导入Jack中的age
cout << age << endl;
return 0;
}
此时编译器报错:
error: 'age' is already declared in this scope
error: redeclaration of 'int Jack::age'
但using编译指令是被允许的:
#include <iostream>
using namespace std;
namespace Jack {
int age = 12;
}
int main()
{
using namespace Jack;
cout << age << endl; // age = 12
int age = 20;
cout << age << endl; // age = 20
cout << Jack::age << endl; // age = 12
}
然而,局部变量会隐藏命名空间中的同名名称。当定义局部变量age之后,使用age,得到的会是局部变量的age。再向使用命名空间中的age,则得利用作用域解析运算符::
。
==需要注意的是:== 使用using编译指令和声明局部变量,二者之间的使用顺序并不会影响上述结果。
结论:using声明像是声明了相同的名称;而using编译指令则更像在包含using声明和命名空间本身的最小声明区域中声明了名称。
相同名称的处理(全局变量)
using 声明:
#include <iostream>
using namespace std;
namespace Jack {
int age = 12;
void greet();
}
int age = 20;
int main(int argc, char *argv[])
{
using Jack::age;
cout << age << endl; // age = 12
cout << ::age << endl; // age = 20
return 0;
}
using编译指令:
#include <iostream>
using namespace std;
namespace Jack {
int age = 12;
void greet();
}
int age = 20;
int main()
{
using namespace Jack;
cout << age << endl;
return 0;
}
编译器报错: error: reference to 'age' is ambiguous
总结:using编译指令将命名空间中的名称视为在函数之外声明的。
作用域
无论using声明还是using编译指令,在A函数中导入的名称,都不能在B函数中使用:
#include <iostream>
using namespace std;
void func();
namespace Jack {
int age = 12;
void greet();
}
int main()
{
using Jack::age;
func();
return 0;
}
void func()
{
cout << age << endl; // 错误用法
}
...
int main()
{
using namespace Jack;
func();
return 0;
}
void func()
{
cout << age << endl; // 错误用法
}
报错: error: 'age' was not declared in this scope
结论
相比using编译指令导入所有名称,使用using声明会好一些。
嵌套式命名空间
C++允许在一个命名空间中嵌套其他命名空间:
#include <iostream>
using namespace std;
namespace outter {
namespace inner {
int num = 512;
}
}
int main()
{
cout << outter::inner::num << endl; // 输出:12
}
也允许在命名空间中使用using声明或是using编译指令:
#include <iostream>
using namespace std;
namespace a {
int A = 512;
}
namespace b {
int B = 1024;
}
namespace c {
using a::A;
using namespace b;
}
int main()
{
cout << c::A << endl;
cout << c::B << endl;
}
可传递性
如果A op B 且 B op C, 则A op C:
#include <iostream>
using namespace std;
namespace c {
int num = 512;
}
namespace b {
using namespace c;
}
namespace a {
using namespace b;
}
int main()
{
cout << a::num << endl; // num = 512
}
或者:
#include <iostream>
using namespace std;
namespace c {
int num = 512;
}
namespace b {
using c::num;
}
namespace a {
using b::num;
}
int main()
{
cout << a::num << endl; // num = 512
}
注意命名空间定义的顺序:
- 如果using编译指令顺序有误报错:
error: 'c' is not a namespace-name
- 如果using声明顺序有误报错:
error: 'c' has not been declared
未命名的命名空间
#include <iostream>
using namespace std;
namespace {
int num = 512;
}
int main()
{
cout << num << endl; // num = 512
}
意义:未命名空间中的名称可当作全局变量使用,但不能在其他文件中使用该命名空间中的名称(类比static修饰的变量)。
命名空间注意事项
注意1
在头文件中定义了变量(未初始化),便不可以在源代码的对应命名空间中为其赋值:
// neamesp.h
#ifndef NAMESP_H
#define NAMESP_H
namespace Jack {
int age;
}
#endif // NAMESP_H
// namesp.cpp
#include "namesp.h"
#include <iostream>
using namespace std;
namespace Jack {
age = 20;
}
int main()
{
cout << Jack::age << endl;
}
报错:‘age’ does not name a type
但可以在头文件中声明一个函数,然后在源代码文件中的对应命名空间中定义:
// namesp.h
#ifndef NAMESP_H
#define NAMESP_H
namespace Jack {
void func();
}
#endif // NAMESP_H
// namesp.cpp
#include "namesp.h"
#include <iostream>
using namespace std;
namespace Jack {
void func()
{
cout << "hello world";
}
}
int main()
{
Jack::func(); // 输出: hello world
}
也可以这样定义:
// namesp.cpp
...
void Jack::func()
{
cout << "hello world" << endl;
}
...
注意2
因为using声明没有描述函数返回类型或是函数特征,所以如果函数被重载,则一个using声明导入全部版本。
指导原则
- 使用在已命名的命名空间中声明的变量,而不是使用外部全局变量
- 使用在已命名的命名空间中声明的变量,而不是使用静态全局变量
- 如果开发了一个函数库或类库,将其放在命名空间中
- 仅将using编译指令当做“兼容旧式代码”的产物
- 尽量不要在头文件中使用using编译指令
- 导入名称时,首选作用域解析和using声明
- 对using声明,首选其作用域为局部而不是全局
还不快抢沙发