目录
一,内联函数inline替换宏函数
内联函数概念
内联函数的特性
二,用const,enum代替宏常量
三,关键词auto
auto的定义
auto不能推导的地方
四,范围for循环
范围for的用法
五,指针空值nullptr
用inline放于函数前面用来修饰的叫做内联函数。
普通函数的痛点:我们都知道,调用函数时要建立栈帧,函数结束后销毁栈帧。建立栈帧要消耗时间和空间。在项目中,同一种函数需要需要调用的次数也就非常大,消耗的栈帧变得非常大。而同一种函数调用所进行的操作是相同的,有没有一种方法能够只需要调用一次就能代替数次相同的调用呢?
在C语言中,设计了一种方案那就是宏,利用宏函数能够代替数次函数的调用。宏函数的编写:
#define add(x , y) ((x)+(y))
宏函数没有像普通函数一样调用栈帧,而是通过一次调用成功了以后,再把整个函数替换到程序中函数的位置
那宏函数有什么缺陷呢?
1,宏不能调试。
我们知道,宏函数在预编译过程就被展开到了各个函数代码段中,编译时无法通过 F11进入函数进行调试,众所周知调试对于写程序是最为重要的一环,缺少调试,错误难以发现。
2,宏没有类型的安全检查,也就变的不安全容易出错。
例如如果我把int型的参数误写成了double型,编译器不会报错。
3,宏函数的编写过于复杂,导致代码可读性差,破坏代码结构。
例如:
#define MAX(a,b)((a)>(b)?(a):(b))
如此简单的函数光是如此多的括号就足以令人眼花缭乱。由于规定,宏函数在定义必须为所有实参加上小括号,否则调用这个宏时会遇到麻烦。
对于上述问题,C++创立了内联inline这个关键字完美解决了宏函数所造成的缺陷,既能获得宏带来的效率,又能改善宏造成的缺陷。只需要在函数名前加上inline
inline max(int a,int b)
{
return a>b?a:b;
}
内联函数和宏函数一样。编译时,调用内联函数的地方会被展开,重复调用时不会反复进行栈帧的创建销毁,提升函数的效率。
1,事实上,inline是一种以空间换时间的做法。在编译阶段,编译器会将代码中调用内联函数的地方展开(相当于把函数的实现复制粘贴到代码中相应位置),会导致代码长度过长,目标文件过大。
2,由于1的原因,内联函数只是给编译器提供的一个建议,一般来说,函数规模较小时并且不是递归且频繁调用的函数时采用inline修饰后才会成为内联函数,否则编译器会自动忽视inline使其变为普通函数。
3,内联函数不支持声明和定义分离
例如在test头文件中首先声明test内联函数
在test源文件中中定义函数test,再在源文件中进行实现:
编译器报错:链接错误。
因为inline被展开后,找不到该函数的地址了,链接报错。
如果要正确使用就应该把内联函数的定义和声明放在一个文件中。
#define ASPECT_RATIO 1.653
我们定义的宏ASPECT_RATIO从未被编译器看到过,因为在预处理阶段,所有的ASPECT_RATIO已经被替换为了1.653,于是ASPECT_RATIO并没有将其加入到符号表中。
但我们在写代码时使用这个常量获得一个编译错误信息时,可能会带来一些困惑,因为这个信息会提到1.653,但是并有提到ASPECT_RATIO如果ASPECT_RATIO被定义在一个不是你写的头文件中,你不知道ASPECT_RATIO也就不知道1.653到底代表着什么。那么追踪错误时会花很多时间。
因此我们用const来代替宏常量
const double ASPECT_RATIO = 1.653;
在该代码中 ASPECT_RATIO肯定会被编译器看到,当然会进入符号表,那么追踪错误就会变得很简单。同时,const相比宏定义还有其他优势:
1)const定义变量存在作用域,而宏常量没有,因为宏只会盲目地进行数值替换。那么宏就不能用来定义class内专属变量,不能提供任何封装性。
2)宏没有类型,无法进行安全检查,而const定义却可以。
auto是自动类型关键词,能自动识别变量类型。对于程序员不需要知道变量的类型就可以进行定义。
其使用方式如下:
注意:auto不能在一行中同时声明两个不同的变量:
c和b所初始化的类型不同,会出错。
1,auto不能作为函数参数
由于函数的建立会开辟栈帧,函数中变量的栈帧必须要提前知道,如果用auto代替。编译器就不知道这个函数要开辟的栈帧有多大。
2,auto不能用来声明数组
例如:编译器报错。
那么auto的实际用法在哪呢?auto能省略变量类型,如果变量类型代码长度过长可以用auto代替。auto还常见用于范围for循环中,就是后文所提到的内容 。
我们在遍历数组时通常用for循环遍历,我们将for循环改为范围for循环,它可以自动判断是否结束。例如:
int arr[5] = {0,1,2,3,4};
for(auto i:arr)
{
cout<
在这段 范围for循环中,第一个i代表迭代的变量,冒号:后的值代表i被迭代的范围。它将arr数组每个变量赋值给i,并将i输出。
如果那你想改变arr数组中的每个值,你可以用auto&取i的地址:
int arr[5] = {0,1,2,3,4};
for(auto& i:arr)
{
i *= 2;
}
同样也可以用相应的类型来代替auto。
int arr[5] = {0,1,2,3,4};
for(int i:arr)
{
cout<
注意:在使用范围for循环时需要确定循环的范围,如果传入的数组范围不够清晰程序将会报错
在C++98中定义了一个叫NULL的关键字。事实上NULL是由宏来实现的,在传统的c头文件中中我们可以看到这样一项:
#define NULL 0
NULL在传统头文件中仅仅是被定义成了0,或者被定义成了无类型指针(void*)常量。而在一些使用中会不可避免的遇到一些麻烦:
NULL成为了一个整型类型的常量0,如果我们要输出f(int*)我们必须要将NULL强制类型转换为int*类型才行,因此在c头文件中增加了一个空指针的定义。如果我们需要用空指针就应该用nullptr而不是NULL。