文章目录
?前言? C++关键字(C++98)? 命名空间?命名空间定义?命名空间使用 ?命名空间的使用有三种方式:?加命名空间名称及作用域限定符?使用using将命名空间中某个成员引入? 使用using namespace 命名空间名称 引入 ?命名空间的查找先后顺序? C++输入&输出?`std`命名空间的使用惯例?总结
?前言
C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式
等。熟悉C语言之后,对C++学习有一定的帮助,本章节主要目标:
域方面、IO方面、函数方面、指针方面、宏方面等。为后续类和对象学习打基础。
? C++关键字(C++98)
C++总计63个关键字,C语言32个关键字
ps:下面我们只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面我们学到以后再
细讲。
语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。我们先来看下C++的历史版本。
asm | do | if | return | try | continue |
---|---|---|---|---|---|
auto | double | inline | short | typedef | for |
bool | dynamic_cast | int | signed | typeid | public |
break | else | long | sizeof | typename | throw |
case | enum | mutable | static | union | wchar_t |
catch | explicit | namespace | static_cast | unsigned | default |
char | export | new | struct | using | friend |
class | extern | operator | switch | virtual | register |
const | false | private | template | void | true |
const_cast | float | protected | this | volatile | while |
delete | goto | reinterpret_cast |
? 命名空间
在C/C++
中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
?命名空间定义
定义命名空间,需要使用到namespace
关键字,后面跟命名空间的名字,然后接一对{}
即可,{}
中即为命名空间的成员。
1. 正确的命名空间定义//正确的命名空间定义namespace Asen{//命名空空间中可以定义变量/函数/类型int rand = 10;int Add(int begin, int end){return begin + end;}struct Node{struct Node* next;int data;};}
命名空间可以嵌套 namespace Asen{int a;int b;int Add(int left, int right){return left + right;}namespace needs_confidence{int Sub(int begin, int end){return begin - end;}}}
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。如:一个工程中的
test.h
和上面test.cpp
中两个asen
会被合并成一个注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
?命名空间使用
命名空间中成员该如何使用呢?比如以下代码运行:
namespace Asen{int a = 0;int b = 1;int Add(int left, int right){return left + right;}namespace needs_confidence{int Sub(int begin, int end){return begin - end;}}}int main(){printf("%d\n", a);return 0;}
运行截图:
?命名空间的使用有三种方式:
?加命名空间名称及作用域限定符
int main(){printf("%d\n", Asen::a);return 0;}
?使用using将命名空间中某个成员引入
using Asen::b;int main(){printf("%d\n", Asen::a);printf("%d\n", b);return 0;}
? 使用using namespace 命名空间名称 引入
using namespace Asen;int main(){printf("%d\n", Asen::a);printf("%d\n", b);Add(66, 88);return 0;}
?命名空间的查找先后顺序
一. 命名空间的查找顺序是:
当前命名空间 > 父命名空间 > 全局命名空间 > 模块命名空间 > 内置模块命名空间
从最具体的当前命名空间开始向上级别进行查找,一直到全局和内置模块命名空间,以此来解决可能出现的同名变量和函数的命名冲突问题。
具体规则如下:
当前命名空间首先在当前命名空间中查找。父命名空间
如果当前命名空间没有找到,则查找其直接父命名空间。嵌套的父命名空间
如果直接父命名空间也没有找到,则继续向上查找更高层次的父命名空间。全局命名空间
如果所有父命名空间都没有找到,则最后在全局命名空间中查找。导入的命名空间
如果使用了using指令导入其他命名空间,也会在导入的命名空间中查找。
举个例子:
namespace A { void func() { // do something }}namespace B { namespace C { void func() { // do something } } using namespace A; void test() { func(); // 会调用B::C::func() }}
这里B
命名空间中的test
函数,首先在B
命名空间中查找func
,没有找到,然后去B
的子命名空间C
中查找,找到就调用C
中的func
。
简单总结
编译默认查找
a、当前局部域 : 自留地
b、全局域找 : 村子野地
c、到展开的命名空间中查找 : 相当于张大爷在自己的自留地加了声明,谁需要就来摘
二. 以下是有关命名空间的查找使用实例:
namespace Asen{namespace needs_confi{int rand = 0; int Add(int left, int right){return left + right;}struct Node{struct Node* next;int data;};}}namespace needs_confi{int rand = 0;int Add(int left, int right){return (left + right) * 10;}}int main(){printf("%p\n", rand);printf("%d\n", Asen::needs_confi::rand);printf("hello world\n");printf("%d\n", Asen::needs_confi::Add(2, 3));printf("%d\n", needs_confi::Add(2, 3));struct Asen::needs_confi::Node pnode;}
三· 展开命名空间暴露问题:
展开命名空间可能会导致的主要问题如下:
当使用using namespace将一个命名空间中的所有名称导入到当前作用域时,如果导入的命名空间和当前命名空间存在同名的变量/函数等,就会产生名称冲突,编译或运行时可能会出现错误。污染全局作用域
使用using namespace后,导入的所有名称都会暴露到全局作用域,可能会与其他代码产生冲突,也更难追踪是哪个命名空间中的名称。难以区分来源
如果直接使用using namespace,在代码中看到一个名称就不知道它来自哪个具体的命名空间。这给代码维护和调试带来困难。性能影响
使用using namespace后,编译器需要在更广泛的作用域中查找名称,这可能会影响编译效率和程序性能。依赖隐藏
使用using namespace可能会隐藏某些依赖关系,例如标准库名称可能会屏蔽用户自定义的同名名称。难以控制
直接使用using namespace没有办法精细控制导入的范围,无法选择性导入某些名称。
因此一般来说,不推荐在头文件中使用using namespace
,在源文件中使用也应谨慎。建议直接使用具体的命名空间限定名称。
如:这里我包含了两个头文件,其中这个#include"Stack.h"
的内容如下,这里主要是用namespace
空间定义,然后展开namespace
,目的是为了看两个命名空间都有相同的内容,在查找时看看会先用哪个?还是会编译报错:
#include"Queue.h"#include"Stack.h"namespace xjh{typedef struct Stack{}ST;void STInit(ST* ps){}struct Queue{//...};}// 展开命名空间 using namespace ahui;using namespace xjh;// 编译默认查找// a、当前局部域: 自留地// b、全局域找: 村子野地// c、到展开的命名空间中查找 : 相当于张大爷在自己的自留地加了声明,谁需要就来摘int main(){struct Stack st1;STInit(&st1);printf("%d\n", sizeof(st1));ahui::ST st;printf("%d\n", sizeof(st));STInit(&st);STPush(&st, 1);STPush(&st, 2);STPush(&st, 3);STPush(&st, 4);return 0;}
代码运行:
? C++输入&输出
和我们刚学C语言时,学习了printf和scanf来进行输出和输入,C++同样也有输入和输出,我们来看下C++是如何来实现问候的。
#include<iostream>// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中using namespace std;int main(){cout<<"Hello world!!!"<<endl;return 0;}
运行图:
说明:
以及按命名空间使用方法使用
std
。cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream >头文件中。<<是流插入运算符,>>是流提取运算符。使用
C++
输入输出更方便,不需要像printf/scanf
输入输出时那样,需要手动控制格式。C++
的输入输出可以自动识别变量类型。实际上cout
和cin
分别是ostream
和istream
类型的对象,>>
和<<
也涉及运算符重载等知识,这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有
一个章节更深入的学习IO流用法及原理。
注意:早期标准库将所有功能在全局域中实现,声明在
.h
后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在
std
命名空间下,为了和C
头文件区分,也为了正确使用命名空间,规定
C++
头文件不带.h
;旧编译器(vc 6.0)
中还支持<iostream.h>
格式,后续编译器已不支持,因此**
推荐使用<iostream>+std
**的方式。 注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应
头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,
规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因
此推荐使用+std的方式。
#include <iostream>using namespace std;int main(){ int a; double b; char c; // 可以自动识别变量的类型 cin>>a; cin>>b>>c; cout<<a<<endl; cout<<b<<" "<<c<<endl; return 0;}
>注意:关于cout
和cin
还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等
等。因为C++兼容C语言的用法,这些又用得不是很多,我们这里就不展开学习了。后续如果有需要,我
们再学习。
?std
命名空间的使用惯例
std
是C++
标准库的命名空间,如何展开std
使用更合理呢?
using namespace std
即可,这样就很方便。using namespace std
展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模
大,就很容易出现。所以建议在项目开发中使用,像
std::cout
这样使用时指定命名空间 +using std::cout
展开常用的库对象/类型等方式。?总结
感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,感谢? ? ? ?,喜欢的话可以点个关注,也可以给博主点一个小小的赞?呀