2022.5.23 C++——C11部分新特性(类型推导(auto),decltype关键字,基于范围for循环等)

23 篇文章 0 订阅

1. 类型推导:auto

C++引入了auto和decltype关键字实现类型推导,通过这两个关键字不仅能方便地获取复杂的类型,而且还能简化书写,提高编码效率。

1.1 c11中auto成为类型指示符

例:
在这里插入图片描述
说明,auto类型推导:auto定义的变量,可以根据初始化的值,在编译时推导出变量名的类型;反之,在没有初始化的时候,是推导不出其类型。
注意:①虽然上面程序中经过const auto* xp = &x;的推导,auto d的类型可以确定为int 了,但是后面还要写着,u=6,不然的话,编译器不予通过,如下:
在这里插入图片描述
②u的初始化不能使编译器推导产生二义性。
由上面的例子可以看出,auto并不能代表一个实际的类型声明(如s的编辑错误),只是一个类型声明的“占位符”。

1.2 auto的推导规则

从1.1中可以看出,auto使用的一些方法,它可以与指针、引用结合起来使用,还可以带上一些cv限定符(const和volatile限定符的统称)
如:

int main()
{
	int x = 0;
	auto* ip = &x;
	auto xp = &x;

	auto& c = x;
	auto d = x;

	const auto e = x;
	auto f = e;

	const auto& g = x;
	auto& h = g;

	return 0;
}

分析:
在这里插入图片描述
总结:
①ip和c的推导过程很显然,auto在编译时被替换为我int,因此ip和c分别被推导为int* 和int &;
②xp的结果说明,其实auto不声明为指针,也可以推导出指针类型;
③d的推导结果说明,当表达式时一个引用类型时,auto会把引用类型抛弃,直接推导出原始的类型int;
④e的结果说明,const auto会在编译时被替换为const int;
⑤f的结果说明,当表达式中带有const(实际上,volatile也会得到同样的结果)属性时,auto会把const属性抛弃掉,推导成non-const类型的int;
⑥g.h的推导说明,当auto和引用(换成指针也会是同样的结果)结合时,auto的推导将保留表达式的const属性。
**注意:**可以得到如下的规则:
(1)当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符后类型一致;
(2)当声明为指针或者引用时,auto推导结果将保持初始化表达式的cv属性。

1.3 auto不能作为函数的形参类型

在这里插入图片描述

1.4 auto的限制

在这里插入图片描述
总结:
①C11中auto成为类型指示符;
②auto不能用于函数参数;
③auto不能用于非静态成员变量;
④auto无法定义数组;
⑤实例化模板时不能使用auto作为模板参数

1.5 auto可以推导函数的返回类型

在这里插入图片描述
从上述结果中可以看出,auto x = my_max(10, 20);这一句根据后面的函数中实参的类型,推出x的类型为int,auto y = my_max(‘a’, ‘b’);根据实参a和b推出y的类型为char。因此,在主函数中用auto,但是调用模板函数时是可以用auto推导出变量的类型。

2. 类型推导:decltype关键字

auto用于通过一个表达式在编译时确定待定义的变量类型,auto所修饰的变量必须被初始化,编译器需要通过初始化来确定auto所代表的类型,即就是必须要定义变量。如果仅希望得到类型,而不需要(或者不能)定义变量的时候就需要用到decltype关键字,这是C11新增的,用来在编译时推导出一个表达式的类型。
语法为:decltype(exp),其中exp表示一个表达式(expression)
从格式上来看,decltype很像sizeof,用来推导表达式类型大小的操作符,其推导过程在编译时完成,并不会真正计算表达式的值。

2.1 普通的decltype用法

在这里插入图片描述

2.2 decltype在函数调用中

在这里插入图片描述
总结:两个例子说明,auto必须要定义变量,所修饰的变量必须初始化,这样能推出变量的类型,并且其值,而decltype不需要定义变量,能推导出变量的类型。

3. 基于范围的for循环

在C98中,不同的容器和数组,遍历的方法不尽相同,写法不统一,也不够简洁,而C11中基于范围的for循环以统一、简洁的方式来遍历容器和数组,操作更方便。
在C++中遍历一个数组的方法一般为

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int len = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	int* ip = NULL;
	for (ip = arr; ip != arr + len; ip++)
	{
		cout << *ip << " ";

	}
	cout << endl;

	return 0;
}

运行结果:
在这里插入图片描述

3.1 在C++基于范围的for循环

在这里插入图片描述
因此,基于范围的for循环一般格式为:

for(ElemType val:array)
{
     ...//循环体
}

其中ElemType是范围变量的数据类型,它必须与数组元素的数据类型一样,或者是数组元素可以自动转换过来的类型;
val是变量的名称,该变量将在循环迭代期间依次接收数组中的元素值,在第一次for循环迭代期间,它接收的是第一个元素的值,在第二次for循环迭代期间,接收的是数组中第二个元素的值,以此类推;
array是要让该循环进行处理的名称,该循环将对数组中每一个元素迭代一次;
循环体是每次循环迭代期间要执行的语句,与其他循环体一样,可以用contiue来结束本次循环,也可以用break跳出整个循环。

3.2可以用auto自动推导出val的数据类型

在这里插入图片描述
注意:
在这里插入图片描述
基于范围for循环中,引用与auto结合,是可行的
在这里插入图片描述

4. 指针空值——nullptr

初始化指针是将其指向一个"空"的位置,比如0。由于大多数计算机系统不允许用户程序写地址为0的内存空间,倘若程序无意中对该指针所指地址赋值,通常在运行时就会导致程序退出。
一般情况下,指针初始化为:

int *ptr = 0;
int *pts = NULL;

在C中可以看到,NULL是一个宏定义
在这里插入图片描述
NULL可能被定义为字面常量,或者定义为无类型指针(void *)0常量,不过无论定义为什么,使用空值的指针时,都会遇到一些麻烦,因此,C++为了更安全一些,避免这些麻烦,在C11中有了nullptr概念。
例:
在这里插入图片描述
总结:我们希望fun(NULL);调用的是fun(char *),但是编译器在编译时将NULL认为是0 ,因此调用了fun(int i),这就引起了字面常量0的二义性,在C98中,字面常量0的类型既可以是一个整型也可以是一个无类型指针(void *)。如果程序员想在代码中调用fun(char *)0,那么就得像最后一个调用一样,将常量0 强转为char类型并调用,否则编译器会优先将0看作一个字面常量0.
在C++11新标准中,处于对兼容性的考虑,字面常量0的二义性并没有消除,但是还是有了新的答案就是nullptr,nullptr是一个所谓“指针空值类型”的常量,指针类型被命名为 nullptr_t,实际上,可以在支持nullptr头文件(cstddef)中找出如下定义:

typedef decltype(nullptr) nullptr_t;

注意:在使用nullptr_t的时候必须加头文件,而nullptr不用,这是由于nullptr是关键字,而nullptr_t是通过推导而来的。
在C11中,sizeof(nullptr)和sizeof((void )0)所占字节数相同,都是4或者8;

为了提高代码的稳健性,在后面的C++编程中,最好使用nullptr。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值