命名空间
命名空间

为什么要使用命名空间?
一个大型的工程往往是由若干个人独立完成的,不同的人分别完成不同的部分,最后再组合成一个完整的程序。由于各个头文件是由不同的人设计的,有可能在不同的头文件中用了相同的名字来命名所定义的类或函数,这样在程序中就会出现名字冲突。不仅如此,有可能我们自己定义的名字会与C++库中的名字发生冲突。
名字冲突就是在同一个作用域中有两个或多个同名的实体,为了解决 ,C++中引入了命名空间,所谓命名空间就是一个可以由用户自己定义的作用域,在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区分它们。
什么是命名空间
命名空间又称为名字空间,是程序员命名的,程序员根据需要指定一些有名字的空间域,把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开。通俗的说,每个名字空间都是一个名字空间域,存放在名字空间域中的全局实体只在本空间域内有效。名字空间对全局实体加以域的限制,从而合理的解决命名冲突。
基本格式
C++中定义命名空间的基本格式如下:
namespace wd
{
int val1 = 0; //不需要table,直接正常顶格写
char val2;
}// end of namespace wd //不需要写;
不需要写;
,写了相当于执行一句空语句
存放的类型
在声明一个命名空间时,大括号内不仅可以存放变量,还可以存放以下类型:
- 常量
- 变量
- 函数,可以是定义或声明
- 结构体
- 类
- 模板
- 命名空间,可以嵌套定义
namespace wd
{
int number = 0;
struct Foo
{
char ch;
int val;
};
void display();
}// end of namespace wd
可见域
定义在名称空间中的变量或者函数都称为实体,名称空间中的实体作用域是全局的, 并不意味着其可见域是全局的。
- 如果不使用作用域限定符和using机制,抛开名称空间嵌套和内部屏蔽的情况,实体的可见域是从实体创建到该名称空间结束。在名称空间外,该实体是不可见的。
命名空间的使用方式
命名空间一共有三种使用方式,分别是using编译指令、作用域限定符、using声明机制
using编译指令
我们接触的第一个C++程序基本上都是这样的,其中std代表的是标准命名空间。
#include <iostream>
using namespace std; //using编译指令
int main(int argc, char *argv[])
{
cout << "hell,world" << endl;
return 0;
}
其中第二行就使用了using编译指令。如果一个名称空间中有多个实体,使用using编译指令,就会把该空间中的所有实体一次性引入到程序之中;对于初学者来说,如果对一个命名空间中的实体并不熟悉时,直接使用这种方式,有可能还是会造成名字冲突的问题,而且出现错误之后,还不好查找错误的原因,比如下面的程序就会报错,当然该错误是人为造成的。
#include <iostream>
using namespace std;
double cout() //error 代码二义性
{
return 1.1;
}
int main(void)
{
cout(); //error 代码二义性
return 0;
}
作用域限定符
第二种方式就是直接使用作用域限定符::啦。每次要使用某个名称空间中的实体时,都直接加上,例如:
namespace wd
{
int number = 10;
void display()
{
//cout,endl都是std空间中的实体,所以都加上'std::'命名空间
std::cout << "wd::display()" << std::endl;
}
}//end of namespace wd
int main(void)
{
std::cout << "wd::number = " << wd::number << endl;
wd::display();
}
这种方式会显得比较冗余,所以还可以采用第三种使用方式。
using声明机制👍
using声明机制的作用域是从using语句开始,到using所在的作用域结束。。
#include <iostream>
using std::cout; //using声明机制
using std::endl;
namespace wd
{
int number = 10;
void display()
{
cout << "wd::display()" << endl;
}
}//end of namespace wd
using wd::number;
using wd::display;
int main(void)
{
cout << "wd::number = " << number << endl;
创建全局变量和全局函数的时候:
- 将自己的全局变量和全局函数放到
匿名命名空间
命名空间还可以不定义名字,不定义名字的命名空间称为匿名命名空间。由于,该空间中的实体,,它只能在本文件的作用域内有效,它的作用域是从匿名命名空间声明开始到本文件结束。在本文件使用无名命名空间成员时不必用命名空间限定。其实匿名命名空间和static是同样的道理,都是只在本文件内有效,无法被其它文件引用。
namespace {
int val1 = 10;
void func();
}//end of anonymous namespace
在匿名空间中创建的全局变量,具有全局生存期,却只能被本文件空间内的函数等访问,。
匿名命名空间的本质是什么?就是将命名空间的名字符号给去掉,让其他文件找不到。
C++ 新的标准中提倡使用匿名命名空间,而不推荐使用static,因为static用在不同的地方,涵义不同,容易造成混淆。另外,static不能修饰class。
命名空间的嵌套及覆盖
int number = 1;
namespace wd
{
int number = 10;
namespace wh //嵌套
{
int number = 100;
void display()
{
cout << "wd::wh::display()" << endl;
}
}//end of namespace wh
void display(int number)
{
cout << "形参number = " << number << endl; //20
cout << "wd命名空间中的number = " << wd::number << endl; //10
cout << "wh命名空间中的number = " << wd::wh::number << endl; //100
cout << "全局number = " << ::number << endl; //1
}
}//end of namespace wd
int main(void)
{
using wd::display;
display(20);
return 0;
}
对命名空间的思考和总结
下面引用当前流行的名称空间使用指导原则:
- 提倡已命名的,而不是直接定义外部全局变量或者静态全局变量。
- 如果开发了一个或者,提倡将其放在一个名称空间中。
- 对于using 声明,首先将其作用域设置为局部而不是全局(临近使用的地方声明)
- 不要在头文件中使用using编译指令,这样,使得可用名称变得模糊,容易出现二义性,
- 包含头文件的顺序可能会影响程序的行为,如果非要使用using编译指令,建议放在所有#include预编译指令后。
课内代码
#include <iostream>
using namespace std;
#if 0 //注释方法
void HWprint()
{
}
void HFCPPProjectPrint()
{
}
#endif
namespace wd
{
//命名空间中可以定义变量、函数、结构体、命名空间,统称为实体
int number = 10;
void print()
{
cout << "void print()" << endl;
}
}//end of namespace wd
/* ; */
int main(int argc, char **argv)
{
cout << "number = " << wd::number << endl;//作用域限定符
wd::print();
return 0;
}
#include <iostream>
using namespace std;//1、using编译指令,可以将命名空间中的实体全部引出来
//自定义的实体与命名空间中的实体冲突
int cout()
{
return 10;
}
int main(int argc, char **argv)
{
cout();
return 0;
}
#include <iostream>
/* using namespace std;//1、using编译指令,可以将命名空间中的实体全部引出来 */
namespace wd
{
//命名空间中可以定义变量、函数、结构体、命名空间,统称为实体
int number = 10;
void print()
{
std::cout << "void print()" << std::endl;
}
}//end of namespace
//2、命名空间+作用域限定符的形式,即使自定义的实体命名空间中的
//实体冲突,一样没有影响
void cout(int x, int y)
{
std::cout << "x = " << x << ", y = " << y << std::endl;
}
int main(int argc, char **argv)
{
std::cout << "number = " << wd::number << std::endl;//作用域限定符
wd::print();
cout(3, 5);
return 0;
}
/*using 声明机制*/
#include <iostream>
using std::cout;//3、using声明机制,一次只引出一个实体
using std::endl;//推荐使用这种
namespace wd
{
//命名空间中可以定义变量、函数、结构体、命名空间,统称为实体
int number = 10;
void print()
{
cout << "void print()" << endl;
}
}//end of namespace
int main(int argc, char **argv)
{
cout << "number = " << wd::number << endl;//作用域限定符
wd::print();
return 0;
}
/*命名空间是可以进行扩展的,结构体是不能进行扩展的*/
#include <iostream>
using std::cout;
using std::endl;
//标准命名空间可以进行扩展
//有可能会与std中的实体冲突,一般不建议进行扩展(标准命名空间中的实体一般都是小写)
namespace std
{
struct MyStruct
{
int ia = 0;
};
}//end of namespace std
#if 0
//结构体是不能进行扩展的
struct A
{
int ia;
};
struct A
{
int ib;
};
#endif
//命名空间是可以进行扩展的
//带命名空间的函数声明
namespace wd
{
void print();
}
namespace hb
{
int number = 20;
void show()
{
cout << "void show()" << endl;
}
void display()
{
cout << "void display()" << endl;
wd::print();
}
}//end of namespace hb
namespace wd
{
int number = 10;
void print()
{
cout << "void print()" << endl;
hb::show();
}
}//end of namespace
int main(int argc, char **argv)
{
cout << "number = " << wd::number << endl;//作用域限定符
wd::print();
return 0;
}
#include <stdio.h>
#include <iostream>
using std::cout;
using std::endl;
int number = 1;
namespace wd
{
int number = 10;
void print(int number)
{
cout << "形参number = " << number << endl;
cout << "命名空间中number = " << wd::number << endl;
cout << "全局number = " << ::number << endl;//匿名命名空间
printf("helloworld\n"); //没有重名,可以直接写,不需要加匿名空间作用域限定符
::printf("helloworld\n");
}
//命名空间是可以嵌套的
namespace wh
{
int number = 200;
}
}//end of namespace
int main(int argc, char **argv)
{
wd::print(20);
cout << "wh::number = " << wd::wh::number << endl;
return 0;
}