一、内联函数(inline)
被inline修饰的函数,会在被调用处展开,没有建立函数栈帧。这一点和C中的宏函数一致,但为什么C++又要再增加inline呢?
我们先来看看宏有哪些的不足吧!
1、代码复杂 可读性低,容易写错
2、无法调试,没有类型安全检查(这是因为宏在预处理阶段就已经被替换掉了)
实际上,其最大的缺点就是第一个,由于宏本质上只是替换,所以它十分容易产生错误,我列举几个常见的错误。
#define ADD(x,y) x+y
int main()
{
int ret = 3*ADD(3, 4);
printf("%d", ret);
return 0;
}
我们定义了一个简单的相加宏函数,最后输出ret,按照我们习惯,ret应该输出的是21。
结果是13,这是为什么呢?本质就是因为宏是个替换,经过替换后ret的右侧变成 3*3+4,这就不难看出答案是13。这告诉我们宏的外侧应该带上括号。
但这样就够了吗?
再看看这样的场景:
#define ADD(x,y) (x+y)
int main()
{
int x = 1;
int y = 1;
int ret = ADD(x&y, x|y);
printf("%d", ret);
return 0;
}
按我们的理解,答案应该是2。
很抱歉再次让大家失望了。为了阐释这个原因,我们再来看看宏替换后的结果。
应该是1&1+1|1,注意到+的优先级是远高于逻辑与和或的优先级。所以最后的结果应该是1&2|1,答案是1。
这个例子告诉我们宏代码内部的变量最好也带上括号,为了就是避免运算符优先级的问题。
宏还有许多的易错场景,我就不多赘述了,回到我们的主题,为什么要引入inline?
原因就是inline修饰的函数可以避免宏函数的这种情况,因为它直接返回了值。
inline修饰的函数具有宏函数的优点,但是它没有了栈帧建立的消耗,也避免了宏函数的缺点,这显然是C++发明者对于宏的优化。
下面我们来实现同样的加法函数,分使用inline修饰与不使用inline修饰,看看调用时候的情况是怎么样的!
当我们不使用inline修饰Add:
从右侧可以看出来Add函数建立了栈帧。
当我们使用inline修饰Add:
对比上面的图,Add函数并没有建立栈帧,而是直接选择在调用的地方展开,确实减少了函数栈帧的消耗。
inline修饰的函数有一些值得注意的地方
1、编译器有权不展开inline修饰的函数,当函数体内部代码太长的时候,编译器往往选择不展开。
2、inline修饰的函数不要定义与声明分离了,会出现链接错误。 因为定义在.cpp文件中声明在.h中 当cpp文件展开.h文件,但是inline修饰的函数被直接展开了,它的地址不会进入符号表当中,所以链接期间就找不到它的地址了。
二、auto修饰的变量
在C++中提供了auto修饰的变量,编译器会自动推导这个变量的类型。
auto&与auto*后面的&与*没有任何的意思,仅仅只是强调它们是引用和指针类型。
注意:当右侧是指针类型的时候auto*与auto修饰没有区别。
下面给出auto使用的几个注意的点:
1、auto不能够修饰数组2、auto类型不能作为函数形参
3、如果一行内有多个被auto修饰的变量,要保证类型相同,否则编译器将会报错。
三、基于范围的for循环
我们将介绍C++中遍历一个数组的方法,具体的细节不多介绍。
四、指针空值nullptr
C++中对于指针空值不再是NULL,而是引入了一个新的关键字nullptr。
由于C++需要兼容C,原本的NULL并没有被删除,它现在只是一个值为0的宏。
而nullptr的底层实现是将0强转为void*类型使它成为一个指针类型。