C++中的内联函数,auto,范围for以及nullptr

一、内敛函数

1.1内联函数的意义

1.1.1C语言中的宏

在C语言中,我们对于多次使用的常量和函数往往会使用宏来定义,但宏使用起来往往十分容易出错,例如我们要定义一个宏函数来求两个整数变量的和

#define ADD(a,b) ((a)+(b))

在这一过程中有几处细节需要注意

①函数内容定义完成以后不可以加分号;

②宏函数内每个变量都要加括号

③宏函数外部总体需要加括号

一旦有哪个细节遗漏,就有可能遇到问题。

1.1.2解释宏函数的细节

我们之前列举了三个需要注意的细节,可是为什么呢?

①宏本质时替换,如果加了分号,可能会出现替换完成后

a=a1+a2;;

这一多重分号的问题,但这还不至于报错,只是如果要打印呢?

cout<<a+b;<<endl;

这种逆语法的语句一定会直接报错。

②宏函数内部如果不加括号,那么倘若出现

ADD(x&y,x|y);

这一类参数中包含运算优先级小于”+“的宏函数调用,就会出现

x&y+x|y;

从而导致x+y被提前计算,结果与预期不符

③宏函数外如果不加括号,那么配合宏函数调用的表达式中出现运算优先级比+高的符号,就会出现问题,如

int n=ADD(x,y)*6;

运算起来会变成这样

int n=x+y*6;

与预期不符。

1.1.3总结宏的优缺点

优点:

①增强代码的复用性

②提高代码性能

缺点:

①不方便调试

②导致代码可读性和可维护性变差

③无安全类型的检查,还容易出错

1.1.4应需求而生的内联函数

C语言中宏的问题带来了种种困扰,因此在C++中,我们替代了它

①用const enum关键字来代替宏定义常量

②用内联函数来代替宏定义短小函数

1.2内联函数使用时的注意问题

1.2.1内联函数使用方式

内联函数本质上是在函数调用的位置把函数内容直接展开,使用inline作为内联标识

inline int Add(int a,int b){//...}

余下的使用与函数一致

1.2.2要明确内联函数只是一个建议

内联函数只适合于语句内容很少且频繁被调用的函数,不包括递归等等复杂函数。

若编译器发现内联函数不符合内容很少的预期,会忽视inline,把函数作为普通函数来对待;

若编译器将函数作为内联函数来处理,那么会在编译阶段用函数体来替代函数调用。

1.2.3其余的注意问题

①本质上内联函数是一种以空间换时间的做法

②内联函数不支持声明和定义分离,因为调用内联函数时本质上不需要跳转操作(编译阶段直接复制指令到调用位置,汇编层面不涉及函数调用),没有对应地址,因此链接的过程会出现无法寻找的问题,因此它的声明和定义我们都放在.h文件中。

二、auto关键字

2.1auto关键字的作用

auto本意为自动推导,根据初始化的值自动推导类型。

2.1-补充说明

谈到替代类型实现定义的作用,C中的typedef也可以达到,但需要注意一些情况下是有问题的

typedef char* pstring;
int main()
{
    const pstring p1;//报错
    const pstring* p2;//此处const修饰的是*p2,即二级指针p2指向的内容,不报错
                      //但此处p2为二级指针,*p2为一级指针且被const修饰,**p2
                      //为char类型的值,可以被修改,与本意仍然不符合
}

这个代码关于p1是会报错的,我们想要定义的其实是

const char* p1;

但是因为pstring是由typedef得来的,所以决定const修饰的内容的指令在寻找到typedef目标之前执行,导致const修饰内容变为了p1,也就是

char* const p1;

此时按规则我们是必须要初始化p1的,所以报错了。

2.2与auto配合使用的函数

函数typeid可以检查auto出来的类型并打印,使用方式

auto b='c';
cout<<typeid(b).name()<<endl;

可以打印出来类型,预计为char

2.3auto与指针和引用的搭配

auto,auto*和auto&使用举例

例如:

int x=10;
auto a=&x;
auto* b=&x;
auto& c=x;

其中

auto a=&x;
auto* b=&x;

这两句其实作用是一致的,但是

①auto*意味着等式右侧必须传的是一个指针,

同理,

②auto&也意味着等式左边一定会是一个别名。

2.4auto不能推导的情况

①不可以一个auto跟多个不同类型变量同时定义

auto a=7,b=6.0;//不被允许

②auto不可以做函数参数

无法对实际类型进行推导,确定传参是否正确

③不可以用来声明数组

auto a={1,2,3};//不被允许

④定义的时候一定要初始化

初始值是推导类型的唯一依据

三、范围for

3.1范围for的意义

加入我们要打印一个数组array,我们要这样做:

for(int i=0;i<sizeof(array)/sizeof(array[0]);i++)
{
    cout<<array[i]<<" ";
}
cout<<endl;

但是当我们用了范围for:

for(auto e:array)
{
    cout<<e<<" ";
}
cout<<endl;

简化了很多很多,实际上范围for自动帮我们把数组中的数据给了e,自动++,自动判断了是否结束。

3.1-补充说明

使用范围for是可以改变数组类型和自动赋值目标变量名的,比如

for(int a:array){
//...
}

也是可以的

3.2使用范围for修改数组中的值

假如我们要把array数组中的每个值扩大为原来的二倍,只需要

for(auto& e:array)
{
    e*=2;
}

在类型后面加上一个引用即可达到目的。

四、nullptr

在C++中,对NULL定义是有缺陷的,查询可见这样的语句

#define NULL 0;

我们知道NULL本身还可以是指针变量的值,那么下述情况

f(int a)
{
    cout<<int<<" ";
}
f(int* a)
{
    cout<<int*<<" ";
}
int main()
{
    f(0);
    f(NULL);
    return 0;
}

会打印成int int的,这很明显不是正常情况。

若要正常我们需要

f((int*)NULL);

为了解决这一问题,C++后期引入了nullptr

它的sizeof值等同于(void*)0,只要使用nullptr就解决了NULL定义为int类型的0导致的一系列矛盾问题

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值