下一篇就要类和对象了,剩了点零碎的知识点就浅浅水一篇把
一. 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。