C++命名空间及using相关用法总结
1.C++命名空间(namespace)
在C++中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象。工程越大,名称相互冲突性的可能性越大。另外,在使用多个厂商的类库时,也可能导致名称冲突。为了避免冲突,在大规模的设计中,以及程序员使用1各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入关键字namesapce(命名空间/名字空间/名称空间),可以更好的控制标识符得到作用域。
#include <iostream>
using namespace std;
//创建一个命名空间
namespace study
{
int a = 10;
}
namespace learn
{
int a = 20;
}
int main()
{
cout << "命名空间study的a="<<study::a << endl;
cout <<"命名空间learn的a="<< learn::a << endl;
}
运行结果输出:
在命名空间下,可以定义函数,变量、结构体、类;除此之外,对于命名空间的定义还应当注意,它必须定义在全局作用域下。
#include <iostream>
#include<string>
using namespace std;
namespace study
{
int a = 10;
namespace A
{
int a = 100;
void sayhi()
{
cout << "hi,Aoki" << endl;
}
struct Student
{
string name;
};
}
}
namespace learn
{
int a = 20;
}
int main()
{
//cout << "命名空间study的a="<<study::a << endl;
//cout <<"命名空间learn的a="<< learn::a << endl;
study::A::sayhi();
cout << "作用域A下的a=" << study::A::a << endl;
}
输出结果:
下面是对命名空间错误定义的举例:
对于命名空间,我们还可以定义无名命名空间,意味着命名空间中的标识符只能在本文件中访问,相当于给这个标识符加上了static,使得其可以作为内部连接。
除此之外,还有就是给命名空间起别名,我们为命名空间A起别名为b,源码如下:
#include <iostream>
#include<string>
using namespace std;
namespace A
{
int a = 100;
void test()
{
namespace b = A;//给命名空间起别名为b
cout <<"A作用域内a="<< b::a << endl;
}
}
namespace //无名(匿名)命名空间
{
int c = 15;
int d = 16;
}
int main()
{
cout << "c=" << c << endl;
cout << "d=" << d << endl;
A::test();
}
运行结果如下图:
下面对命名空间namespace的用法总结如下:
- 命名空间的用途:解决名称冲突问题
- 命名空间下可以定义函数、变量、结构、类
- 命名空间必须定义在全局作用域下
- 命名空间可以嵌套命名空间
- 命名空间是开放的,可以随时给原先的命名空间添加内容
- 定义另个名字相同的命名空间时,两个命名空间会合并,但是不会覆盖
- 当我们定义了无名空间时,相当于写了
static intc=15,static int d=16
,只能在当前文件内使用
2.using声明
using声明可使得指定的标识符可用
#include <iostream>
using namespace std;
namespace One
{
int test = 100;
}
void test()
{
int test = 100;
using One::test;//using声明
cout << "test=" << test << endl;
}
int main()
{
test();
}
此处我们使用了using声明,但是编译却发生了错误。因为在test()中我们定义了一个整型数test,并赋予了初值。当我们使用using声明时,编译器有就近原则,它会选择输出test中定义的整型数,但是又发现有using声明,出现二义性问题,编译器不知道怎么处理,所以报错。报错信息如下:
所以,在using声明中,我们应当注意避免二义性问题。
#include <iostream>
using namespace std;
namespace One
{
int test = 100;
}
void test()
{
int test = 100;
//using One::test;//using声明
cout << "test=" << test << endl;
}
int main()
{
test();
}
输出结果如下图:
using声明的一个例子:
#include <iostream>
using namespace std;
namespace One
{
int test = 100;
int a = 99;
int b = 30;
void func()
{
cout << "hi,Aoki!" << endl;
}
void func1()
{
cout << "hello,Aoki!" << endl;
}
}
void test()
{
cout << "test=" << One::a << endl;
//using声明
using One::a;
using One::func1;
using One::func;
cout << "a=" << a << endl;
func1();
func();
}
int main()
{
test();
}
运行结果如下图:
下面是using声明遇到函数重载的一个例子:
#include <iostream>
using namespace std;
namespace Two
{
void func()
{
cout << "Nothing!" << endl;
}
void func(int x)
{
cout << x << endl;
}
void func(int x, int y)
{
cout << "x=" << x ;
cout << ",y=" << y << endl;
}
}
void test1()
{
using Two::func;
func();
func(15);
func(15, 20);
}
int main()
{
test1();
}
输出结果如下图:
如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合。
3.using编译指令
using编译指令使整个命名空间标识符可用。
#include <iostream>
using namespace std;
namespace One
{
int a = 99;
int b = 30;
void func()
{
cout << "hi,Aoki!" << endl;
}
void func1()
{
cout << "hello,Aoki!" << endl;
}
}
void test()
{
using namespace One;
cout << a << endl;
cout << b << endl;
func();
func1();
int a = 100;//没有产生二义性
cout << a << endl;
}
namespace Two
{
int a = 1;
int b = 2;
};
void test1()
{
//产生二义性
using namespace One;
using namespace Two;
cout << a << endl;
}
当我们像上面一样使用using编译指令时,会出现二义性问题,编译器无法通过编译。错误信息如下:
当我们使用using声明或using编译指令时,会增加命名冲突的可能性。也就是说,如果有命名空间,并在代码中使用作用域解析运算符,则不会出现二义性问题。
例子如下:
#include <iostream>
using namespace std;
namespace One
{
int a = 99;
int b = 30;
void func()
{
cout << "hi,Aoki!" << endl;
}
void func1()
{
cout << "hello,Aoki!" << endl;
}
}
void test()
{
using namespace One;
cout << a << endl;
cout << b << endl;
func();
func1();
int a = 100;
cout << a << endl;
}
namespace Two
{
int a = 1;
int b = 2;
};
void test1()
{
using namespace One;
using namespace Two;
//cout << a << endl;
}
int main()
{
test();
test1();
}
输出结果如下:
当我们引入一个全局的using编译指令时,就为该文件打开了命名空间,它不会影响其他的文件,所以可以再每一个实现文件中调整对命名空间的控制。比如,如果发现某一个实现文件中有太多using指令而产生命名冲突,就要对该文件做个简单的改变,通过明确限定或者using声明来消除名字冲突,这样不需要修改其他实现文件。