前引:C++作为C语言的继承者,也是其掘墓人。在编程语言的演化长河中,C++始终游走在【兼容】与【革新】的路上。C程序员眼中(高效直接)的全局函数,对于C++开发者来说是【命名空间污染的炸弹】,如果C++未发明命名空间,今天的Linux内核是否还会选择extern“C”包裹所有的C++代码。下面我们来看看命名空间如何构建模块化生态~
目录
命名空间介绍
命名是(Namespace)是C++提供的一种代码隔离机制,用于将全局作用域划分为逻辑独立的区域,避免不同模块或库中的同名标识符(变量、函数、类等)发生冲突,一个命名空间就定义了一个新的作用域
例1:A程序员与B程序员现在分模块完成某个工程,A程序员定义了变量tmp ,B程序员也定义了 变量tmp,最后二者模块合并时,可能导致重定义变量报错
例2:C++库中可能定义了某个关键字,但是程序员也可能定义了某个与库中一模一样的变量,这 就无法通过编译
如何声明命名空间
命名空间应该在全局定义,而不是局部
基础命名空间
命名空间使用关键字 namespace
定义,后面跟上命名空间的名字,然后是一对大括号 {}
,括号的末尾不用加分号,其中包含命名空间的成员。命名空间可以定义变量、函数、子命名空间、类型等
例如小编定义了一个tmp变量:
namespace MyNamespace
{
int tmp = 0;
}
例如定义函数、变量、结构体:
namespace MyNamespace
{
//定义变量
int tmp = 0;
//定义函数
int Add(int a, int b)
{
return a + b;
}
//定义结构体
struct List
{
struct List* next;
int data;
};
}
嵌套命名空间
顾名思义,嵌套命名空间就是在基础命名空间里面在定义命名空间,例如:
namespace MyNamespace
{
//定义变量
int tmp = 0;
//定义函数
int Add(int a, int b)
{
return a + b;
}
//定义结构体
struct List
{
struct List* next;
int data;
};
//嵌套命名空间
namespace YourNamespace
{
int a = 10;
}
}
匿名命名空间
顾名思义,匿名就是不去给这个命名空间设置名字,它的作用域仅在当前的编译单元(如:.cpp文件)内可见,例如:
//匿名命名空间
namespace
{
//定义数组
int arr[10] = { 0 };
}
如何使用命名空间
完全限定访问
这种是访问类型是限定区域的,现用现拿,方式:空间名+双冒号+空间里面的成员对象
例如:
//完全限定访问
int cur = MyNamespace::Add(5, 5);
嵌套空间的使用也一样:空间名+双冒号 + 使用对象(只不过这里的对象是另一个命名空间)
//完全限定访问
int cur = MyNamespace::YourNamespace::a;
using 声明(局部引入)
相当于在局部告诉编译器,你要在这个局部使用这个空间里的某个对象,它相较于完全限定访问,范围稍微放大了一点,例如:
//using 声明
using MyNamespace::tmp;
//可以直接使用tmp
int cur = tmp;
using namespace指令(全局引入)
这个很危险!属于 全局性的!导入定义的空间,就可以全局直接使用,例如:
//全局导入使用
using namespace MyNamespace;
同样跨文件也是可以的,例如:
回顾整理
首先我们定义了某个命名空间,它的使用是很自由的
你可以选择限定访问,即仅仅对某个对象使用
还可以在某个函数内展开这个空间,那么这个空间仅仅在这个函数内生效
最后是最危险的行为!直接全局展开使用,包括跨文件。这样就无限制了
标准库的命名空间
我们既然可以自己命名空间,C++也有它自己的命名空间,我们称为C++标准库
C -> C++头文件的变化:
早期标准库将所有功能在全局域中实现,声明在 .h 后缀的头文件中,使用时只需包含对应头文即可
后来将其实现在 std 命名空间下,为了和 C 头文件区分,也为了正确使用命名空间, 规定C++头文件不带 .h
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
使用C++标准库进行输入输出:
1. 使用 cout 标准输出对象(控制台)和 cin 标准输入对象(键盘)时,必须包含< iostream >头文件 以 及按命名空间使用方法使用std
2. cout 和 cin 是全局的流对象,endl 是特殊的C++符号,表示换行输出,他们都包含在包含
< iostream >头文件中
3. << 是流插入运算符, >> 是流提取插入符
4. 使用C++输入输出更方便,不需要像 printf/scanf 输入输出时那样,需要手动控制格式。 C++的 输入输出可以自动识别变量类型,这时候就可以用流提取运算符
5. 实际上 cout 和 cin 分别是 ostream 和 istream 类型的对象,>>和<<也涉及运算符重载等知识, 这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用
头文件与 std 的关系
C++原生头文件<iostream>,所有内容均在 std 内,使用时需要按命名空间使用方法使用std
比如我们使用<iostream>就可以代替原来的C中的各种头文件,例如:
cout 、cin、 endl 的深度解析
注意:使用 cout 标准输出对象(控制台)和 cin 标准输入对象(键盘)时,必须包含< iostream >头文件 以及按命名空间使用方法使用std
(1)std :: cout 表示标准输出流
输出通常缓存在内存中,直到遇到换行符 \n 或者 std::flush 显示刷新
一般搭配 << (流插入运算符)使用,支持链式调用,例如:
//链式输入
std::cout << "tmp:" << 42 << std::endl;
//非链式输入
std::cout << "tmp" << std::endl;
下面两种情况是区别不大的,加了 endl 只是可以换行,刷新c++的缓冲区的内容到文件
(2)std :: endl 流操纵符
它有两个作用:1、插入换行符“\n” 2、强制刷新输出缓冲区,即调用 std :: flush
频繁使用可能导致 I/O 性能下降,建议使用 \n 代替,例如:
(3)std :: cin 标准输入流
功能:从控制台读取用户输入
>> 是流提取运算符,自动跳过空白字符
C++的输入输出可以自动识别变量类型,这时候就可以用流提取运算符
例如:
当流输入时不要加 std::endl ,可以这么理解:既然需要用户输入,加了endl 结束了还怎么输入?
常见错误
(1)没有用 std ::前缀
(2)随便使用 using namespace std
如果程序员定义了变量 rand ,但是C++标准库中也有对于 rand 的定义,这就导致了冲突报错
(3)输入与输出类型不匹配
总结
(1)std 是标准库的命名空间,任何标准功能都需要通过 std:: 访问
(2)需要清楚 cout、cin、endl、std 命名空间的对象
(3)尽量少用全局的 using namespace std ,使用优先顺序:全限定 -> 局部 -> 全局
【雾非雾】期待与你的下次相遇!