#简介#
主要作为自己学习的复习使用
#1.命名空间-namespace#
1.1Cpp引入命名空间的原因:
在C语言的日常使用中,无法随心所欲得定义变量名称,例如rand,因为在头文件stblib.h中,有rand()这样一个函数.此外同一个项目中,不同部分可能由不同人的维护,最后整合到一起时,可能会导致变量名称的冲突.
1.2域的概念
1.2.1域的种类
在C语言中,我们了解到有全局域,局部域等,Cpp引入的命名空间,则有命名空间域,之后我们会学习Cpp中的类,类还有类域
1.2.2域的特点
在不同的域中,我们能够命名相同名称的变量.
1.2.3域的访问

如图所示,在全局域和局部域中,都有一个a.那么printf优先访问谁呢?

域访问的顺序:局部域->全局域
1.3域作用限定符(::)
如果我想要访问特定域的变量,这时候就需要用到域作用限定符 (::) .

使用规则: 域名 :: 变量名 如果域名为空,代表全局域
1.4命名空间的定义

命名空间的定义,名字xy随意取
1.5命名空间的展开
我们可以通过域作用限定符来定向的访问某个域中的某个变量,我们也可以将某个域完全展开,来访问变量.
![]()
注意点:一般来说,命名空间都定义在main函数外面,将命名空间展开时,可以理解成将命名空间里的变量添加到了全局域中.
另外,命名空间的展开与#include头文件是有区别的,#include 头文件,是等同于将头文件的内容包含到当前文件(拷贝),而展开:允许编译器去搜索.
1.6一些零碎的补充
1.6.1命名空间可以包含变量,函数,结构体等.
1.6.2格式上命名空间不带分号;
1.6.3命名空间是可以嵌套的
1.6.4命名空间的合并
![]()
如果我有两个文件中,都定义了namespa xy.Queue.h可以调用Stack.h中xy中的变量,反之同理.
#2.缺省参数(默认参数)#
2.1缺省参数的定义:
在定义函数的时候给形参一个默认值,如果调用函数时传入参数就使用传入的参数,如果不传入参数,就使用给的默认参数(缺省参数).

2.2全缺省与半缺省
2.2.1全缺省

当函数有多个参数,并且每一个参数都有缺省值.

注意:全缺省传参不能跳跃传参.
2.2.2半缺省
由2.1可知,有缺省值时,传参不能跳跃传参,所以,半缺省有规定:从右往左依次可以有缺省值.

2.3缺省的应用--控制动态静态栈的实现(结合C语言的简易版)


2.4缺省的声明定义问题
![]()
以实现栈(C语言版本)举例,一般情况下,我们会把函数的声明放在Stack.h中,然后在Stack.c中去定义我们的函数,最终在Test.c中去调用我们的函数.
但是有两处地方,缺省该怎么给呢?
在Cpp中为了避免声明定义给出不同的缺省值,规定,缺省写在声明处.
此外,在Test.c中一般都是包含Stack,h文件,程序先访问的是声明.
#3.函数重载#
3.1函数重载的定义:
在Cpp中可以定义同名的函数,但是参数的类型,数量,类型顺序需要不同.
3.2Cpp支持函数重载的原因----C为什么不支持函数重载
我们需要从编译链接的角度来看,在vs2022下(或者其他可能相似的编译器下)过程如下:
3.2.1预处理
编译链接一段程序时,编译器会先进行预处理,比如将头文件展开,宏进行替换, 去掉注释.相等于整合代码,并生成新文件,这里我以栈举例---Stack.c和Test.c最后生成Stack.i和Test.i文件
3.2.2编译
编译阶段会对代码的语法进行检查,如果语法无误,就会将代码编写成汇编代码,并生成Stack.s和Test.s文件
3.2.3汇编
硬件Cpu无法读懂汇编代码,只能读懂二进制的机器码,所以要将汇编代码转化成二进制机器码.并生成Stack.o和Test.o文件.
3.2.4链接--核心原因所在
链接过程是将Stack.o和Test.o文件链接,并生成可执行的XXX.exe文件.具体链接过程:如果Test.c文件中调用了Stack.c中的某个函数,在汇编代码中会产生call的指令.一般 call 函数名(地址) ,call之后会通过jump指令进入函数内部进行汇编代码执行.(在连接过程中,Stack.o会产生一个符号表,这个函数名(地址)就存在这个符号表中).
在C语言中,这里的函数名,就是函数本身的名字,例如定义了Func,符号表里面就是Func
但是在Cpp中,这里的函数名,不仅仅是本身名字(函数名修饰规则),例如:

在func后面,还有形参数据类型的缩写(i-int , d-double).
由此我们就可以得知原因.在C语言中,如果函数同名(重载),会导致call的对象不明确.
但是cpp中由于其特殊的函数名修饰规则,函数重载,虽然同名,但是call的对象名是不同的.
3.2.5补充:
前提:Stack.h,Stack.c,Test.c, Test.c中包含了栈的头文件
在编译阶段生成汇编代码时,就需要call 函数名(地址),但是在之后的链接阶段才通过符号表找到地址.-------这个地址要是在Stack.c中定义的函数的地址,不是声明的地址.但是是没有问题的,因为有声明的存在,所以保证了在链接阶段可以在符号表找到这段地址,暂时缺失是没问题的.
#4.引用#
4.1引用的定义
在cpp中,引进了一种新的数据类型-引用(&),可以理解成 取别名.

可以发现,其特点是:在同一块空间上,同加同减
4.2引用的指向问题
cpp规定引用是不能改变对象的
int a=0;
int& b=a;
int x=1;
b=x;
这里b=x,是将x赋值给b,而不是说b的对象更改为x
4.3引用实现传值交换

结合重载

4.4引用作为返回值
4.4.1在研究引用作返回值之前,先看一看int作返回值的情况
int Func()
{
int n=0;
return n;
}
int main()
{
int ret=Func();
return 0;
}
底层逻辑:
首先在函数调用时,会创建栈帧,而函数里的变量n就存在栈帧中,当函数调用结束时,栈帧销毁,n也就没了.那是如何返回的呢?其实,在栈帧销毁前,编译器会把返回值n给到一个创建的临时变量中,再由这个临时变量赋值给ret.
4.4.2
int& Func()
{
int n=0;
return n;
}
int main()
{
int ret=Func();
return 0;
}
这里相当于是返回了n的别名(引用),理论上就是应该n直接赋值给ret.(没有了创建临时变量的过程),但是,当Func()调用完成后,n就被销毁了.所以这样写不对的
int& Func()
{
static int n=0;
return n;
}
int main()
{
int ret=Func();
return 0;
}
我们将n放入静态区,栈帧被销毁,n也没有被销毁,所以就可以了.
至于为什么要这样做:有些时候可能返回一个大对象,如果创建临时变量返回这个大对象,很亏空间
4.5引用的底层
在语法层面上,引用是不开空间的,是变量的别名而已.但是从汇编层面上,引用是开空间的.所以,引用本质上跟指针差不多.
#5.const修饰的权限问题#
5.1权限的放大缩小平移
cpp规定权限只能缩小或者平移,不能够放大
放大:
const int a=0;
int& b=a;
b没有被const限制,可以被修改,相当于权限被放大
缩小:
int a=0;
const int& b=a;
a++;
注意:这里权限缩小后,a可以++,b不可以,但是a++变成1后,b也跟着变成1
平移
int a=0;
int& b=a;
int& c=b;
5.2常量常性
5.2.1给常量取别名
const int& a=10;
常量是无法改变的,所以一定要加const,不然权限就被放大了
5.2.2临时变量的常性
double d=1.4;
int a=d;
首先,在这个过程会发生整形提升(这里不说了),主要的是会创建一个临时变量,d先赋值给临时变量(整形提升的一些过程)然后,临时变量赋值给a,这里的临时变量是有常性的.
所以
double d=1.4;
int& a=d;
这个代码是错误的,本质上,是临时变量赋值给a,但是临时变量有常性,所以发生了权限的放大
double d=1.4;
const int& a=d;
主要发生了类型的转变,就会有临时变量,之前函数返回值中,也有临时变量.
int func()
{
static int x=0;
return x;
}
int main()
{
int& ret=func();
return 0;
}
这里同理,相当于ret是临时变量的别名,但是权限被放大了
#6.内联函数inline#
6.1
inline int Add(int x,int y)
{
return x+y;
}
类似于宏函数,将函数展开,不用建立栈帧,适合短小,多次调用的函数
但是不能所有的函数都设置成内联函数,会导致可执行文件变的很大-代码膨胀.
inline只是对编译器的一个建议,是否展开由编译器决定.
vs的debug版本是用不了inline的,因为用了就无法调试了(可以自己修改)
6.2内联函数的定义和声明
只在声明处说明inline
会报链接错误,因为内联函数在链接时不进入符号表,链接不到.所以我们将声明和定义不分开,都写在头文件中,在编译阶段生成完整的汇编代码.

#零碎的补充#
1.cpp标准库std
1.1展开std库
![]()
但是注意:直接展开有一定的风险,命名重复----项目里建议不要展开,建议指定访问或者部分展开
1.2命名空间的部分展开
![]()
2.cpp的输入输出--IO流
2.1头文件 #include <iostream>
2.2 流插入cout-----相当于C语言中的printf

其中endl代表换行
cout相比于printf的优点:自动识别数据类型
3.流提取cin
cin相当于c语言中的scanf

cin这行代码需要输入两个值,分别赋值给x和y
4.printf和scanf 比 io流快
cpp兼容c,cpp不仅要检查io流的缓冲区,还要检查printf和scanf的缓冲区.
5.auto关键字
auto可以自动识别数据类型
语法糖:
for(auto e : arr){}自动循环数组,将arr数组的值依次赋给e,arr一定要代表整个数组,不能是指针.
变种 for(auto& e:arr){}
6.宏函数
宏函数的本质是替换,优点:不用建立栈帧,适合短小,多次调用的函数

7.nullptr
cpp将0和NULL被归为int类型--0可能区别不开,所以有个void* 的nullptr(新的空指针)
8.cpp仅有类型的函数
![]()


被折叠的 条评论
为什么被折叠?



