auto关键字
auto简介
在早期的C/C++中auto的含义是:使用auto修饰的变量是具有自动存储器的局部变量,但遗憾的是一直没有人去使用它。
在C++11中,标准委员会赋予了auto全新的含义:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
#include <iostream> using namespace std; double Fun() { return 3.14; } int main() { int a = 10; auto b = a; auto c = 'A'; auto d = Fun(); //打印变量b,c,d的类型 cout << typeid(b).name() << endl;//打印结果为int cout << typeid(c).name() << endl;//打印结果为char cout << typeid(d).name() << endl;//打印结果为double return 0; }
测试结果:
注意:使用auto变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此,auto并非是一种“类型”的声明,而是一个类型声明的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
auto的使用规则
一、auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时必须加&。
#include <iostream> using namespace std; int main() { int a = 10; auto b = &a; //自动推导出b的类型为int* auto* c = &a; //自动推导出c的类型为int* auto& d = a; //自动推导出d的类型为int //打印变量b,c,d的类型 //typeid().name() 可以查看类型 cout << typeid(b).name() << endl;//打印结果为int* cout << typeid(c).name() << endl;//打印结果为int* cout << typeid(d).name() << endl;//打印结果为int return 0; }
测试结果:
注意:用auto声明引用时必须加&,否则创建的只是与实体类型相同的普通变量。
二、在同一行定义多个变量
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
int main() { auto a = 1, b = 2; //正确 auto c = 3, d = 4.0; //编译器报错:“auto”必须始终推导为同一类型 return 0; }
测试结果:
auto不能推到的场景
1.auto不能作为函数的参数
以下代码编译失败,auto不能作为形参类型,因为编译器无法对x的实际类型进行推导。
void TestAuto(auto x) {}
2.auto不能直接用来声明数组
int main() { int a[] = { 1, 2, 3 }; auto b[] = { 4, 5, 6 };//error return 0; }
基于范围的for循环(C++11)
范围for的语法
若是在C++98中我们要遍历一个数组,可以按照以下方式:
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //将数组元素值全部乘以2 for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { arr[i] *= 2; } //打印数组中的所有元素 for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { cout << arr[i] << " "; } cout << endl;
以上方式也是我们C语言中所用的遍历数组的方式,但对于一个有范围的集合而言,循环是多余的,有时还容易犯错。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //将数组元素值全部乘以2 for (auto& e : arr) { e *= 2; } //打印数组中的所有元素 //依次取数组中的数据赋值给e //自定判断结束 //自动迭代 for (auto e : arr) { cout << e << " "; } cout << endl;
范围for的使用条件
一、for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
二、迭代的对象要实现++和==操作
这就涉及到迭代器的问题,大家先了解一下就行。
指针空值nullptr
C++98中的指针空值
在良好的C/C++编程习惯中,在声明一个变量的同时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误。比如未初始化的指针,如果一个指针没有合法的指向,我们基本都是按如下方式对其进行初始化:
int* p1 = NULL; int* p2 = 0;
NULL其实是一个宏,在传统的C头文件(stddef.h)中可以看到如下代码:
/* Define NULL pointer value */ #ifndef NULL #ifdef __cplusplus #define NULL 0 #else /* __cplusplus */ #define NULL ((void *)0) #endif /* __cplusplus */ #endif /* NULL */
可以看到,NULL可能被定义为字面常量0,也可能被定义为无类型指针(void*)的常量。但是不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,例如:
#include <iostream> using namespace std; void Fun(int p) { cout << "Fun(int)" << endl; } void Fun(int* p) { cout << "Fun(int*)" << endl; } int main() { Fun(0); //打印结果为 Fun(int) Fun(NULL); //打印结果为 Fun(int) Fun((int*)NULL); //打印结果为 Fun(int*) return 0; }
程序本意本意是想通过Fun(NULL)调用指针版本的Fun(int* p)函数,但是由于NULL被定义为0,Fun(NULL)最终调用的是Fun(int p)函数。
注:在C++98中字面常量0,既可以是一个整型数字,也可以是无类型的指针(void*)常量,但编译器默认情况下将其看成是一个整型常量,如果要将其按照指针方式来使用,必须对其进行强制转换。
C++11中的指针空值
对于C++98中的问题,C++11引入了关键字nullptr。
注意:
1、在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为关键字引入的。
2、在C++11中,sizeof(nullptr)与sizeof((void*)0)所占的字节数相同。
3、为了提高代码的健壮性,在后序表示指针空值时建议最好使用nullptr。