《C++详解》(三)用const和inline代替宏函数,关键字auto,范围for循环和指针空值nullptr

目录

一,内联函数inline替换宏函数

内联函数概念

内联函数的特性

二,用const,enum代替宏常量

三,关键词auto

auto的定义

auto不能推导的地方

四,范围for循环 

范围for的用法

五,指针空值nullptr


一,内联函数inline替换宏函数


   内联函数概念

    用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被展开后,找不到该函数的地址了,链接报错。

如果要正确使用就应该把内联函数的定义和声明放在一个文件中。

二,用const,enum代替宏常量


#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的定义

auto是自动类型关键词,能自动识别变量类型。对于程序员不需要知道变量的类型就可以进行定义。

其使用方式如下:

 注意:auto不能在一行中同时声明两个不同的变量:

c和b所初始化的类型不同,会出错。

auto不能推导的地方

1,auto不能作为函数参数

   由于函数的建立会开辟栈帧,函数中变量的栈帧必须要提前知道,如果用auto代替。编译器就不知道这个函数要开辟的栈帧有多大。

2,auto不能用来声明数组

   例如编译器报错。

  那么auto的实际用法在哪呢?auto能省略变量类型,如果变量类型代码长度过长可以用auto代替。auto还常见用于范围for循环中,就是后文所提到的内容 。


四,范围for循环 


范围for的用法

我们在遍历数组时通常用for循环遍历,我们将for循环改为范围for循环,它可以自动判断是否结束。例如:

int arr[5] = {0,1,2,3,4};
for(auto i:arr)
{
  cout<<i<<endl;

  }

    在这段 范围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<<i<<endl;

  }

注意:在使用范围for循环时需要确定循环的范围,如果传入的数组范围不够清晰程序将会报错


五,指针空值nullptr

    在C++98中定义了一个叫NULL的关键字。事实上NULL是由宏来实现的,在传统的c头文件中中我们可以看到这样一项:

#define NULL 0

    NULL在传统头文件中仅仅是被定义成了0,或者被定义成了无类型指针(void*)常量。而在一些使用中会不可避免的遇到一些麻烦:

    NULL成为了一个整型类型的常量0,如果我们要输出f(int*)我们必须要将NULL强制类型转换为int*类型才行,因此在c头文件中增加了一个空指针的定义。如果我们需要用空指针就应该用nullptr而不是NULL。

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值