C++入门——auto、范围for、nullptr

下一篇就要类和对象了,剩了点零碎的知识点就浅浅水一篇把


目录

一.   auto关键字

二.   范围for

三.   nullptr


一.   auto关键字

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的
是一直没有人去使用它,这是由于变量本身就具备生命周期,使用auto修饰函数实在是多余。

而在C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

简单来说,其实auto声明的变量可以依照初始化的内容来自动识别类型。

int TestAuto()
{
    return 10;
}
int main()
{
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto* e = &a;//等价于auto e=&a;
    auto d = TestAuto();
    return 0;
}

而为了验证这一点,我们可以使用typeid关键字来得到各个变量的类型

int TestAuto()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = TestAuto();
	auto e = &a;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	cout << typeid(e).name() << endl;
	return 0;
}

 注意

1.使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编
译期会将auto替换为变量实际的类型。

因此,auto也不能作为函数的参数

2.在上面我们说到,auto声明指针类型时,auto与auto*没有区别,但是,在声明引用类型时,必须使用auto&

	int a = 10;
	auto& b = a;
	cout << typeid(b).name() << endl;

但由于一些不可控的因素(我也不知道为什么),打印类型时依旧时int

但改变b时a也会改变,这也就证实了b是a的别名,这里就不多做讨论了

3.同样,auto也会识别const修饰的类型,也可以使用const修饰auto

	const int a = 10;
	auto b = a;
	const auto c = a;

4.而在同一行定义多个变量时,变量的类型需要一致,这是由于auto只对第一个类型进行推导,用推导出来的类型定义该行所有变量

5.还有一点就是,auto不能直接用来声明数组,这一点当做规定记住即可



二.   范围for

在c语言的学习中,经常需要去遍历数组

int nums[5] = { 1,2,3,4,5 };
for (int i = 0; i < 5; i++)
{
	printf("%d", nums[i]);
}

而在遍历时,往往格式都是固定的,在写循环范围时可能出错

因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范
围内用于迭代的变量,第二部分则表示被迭代的范围。

概念可能看不太懂,我们直接改写一下上面的遍历

	int nums[5] = { 1,2,3,4,5 };
	for(int e:nums)
	{
		cout << e << " ";
	}

大致理解一下,便是将数组的每一个元素依次赋给变量e,并做出相应的操作

而我们也可以使用auto来识别数组的类型,进而增加这段代码的普遍性 

而在进行数组遍历时,有时会对这个数组进行改变,例如我们想让数组的每个元素加一

若我们依旧采用上述方式

	int nums[5] = { 1,2,3,4,5 };
	for(auto e:nums)
	{
		e++;
	}
	for(auto e:nums)
	{
		cout << e << " ";
	}

我们会发现数组没有被改变。

这是因为,这里的e只是数组中每个元素的拷贝,相当于函数中的传值调用,无法做到改变。而在函数传参中,我们可以使用指针或引用,而在这里,由于赋值的是数组中的每个元素,而不是地址,所以不能使用指针,只能使用引用

	int nums[5] = { 1,2,3,4,5 };
	for(auto& e:nums)
	{
		e++;
	}
	for(auto e:nums)
	{
		cout << e << " ";
	}

而在范围for内的数组,我们需要的是整个数组,而若是在函数内范围for使用传参传入的数组,由于会发生退化,数组名传参后会变为指针类型,所以会出现错误



三.   nullptr

在C语言指针的使用中,我们有时会将指针初始化为NULL

int* p=NULL;

在本质上,NULL是一个宏定义

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

在c语言中

#define NULL (void* (0) )

这是没有什么问题的

而在C++中

#define NULL 0

内部是这样的,这也就使得int* p=NULL与int* p=0本质上是一样的

这种用法在大多时候是没有问题的,但有时还是会出现问题

例如以下的函数重载

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

正如我们上面所讲的, 即使第二中传参我们想要传的是指针,但本质上传的还是0,因此会调用第一个函数

为了解决类似的问题,C++11引入了新关键字nullptr,表示指针空值

void f(int)
{
    cout<<"f(int)"<<endl;
}
void f(int*)
{
    cout<<"f(int*)"<<endl;
}
int main()
{
    f(0);
    f(nullptr);
    return 0;
}

这样便能解决上面的问题 

而为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
 

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

finish_speech

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值