Ⅲ. 范围 for(C++11)
0x00 概念
📚 范围 for,即 —— 基于范围的 for 循环。
范围for可以说是一颗 "语法糖" ,什么是语法糖?
就是用起来会让人觉得很甜,很爽的东西~
以前,我们要遍历一个数组,一般会按照以下方式进行:
int main() { int arr[] = { 1, 2,3,4,5 }; int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组大小 int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
对于一个有范围的集合而言,让程序员来说明循环的范围是多余的,
有时候还会容易犯错误……
因此,C++11中引入了基于范围的 for 循环。
📚 语法: for ( auto 变量名 : 数组)
for 循环后的括号由冒号分为两部分:
第一部分:范围内用于迭代的变量
第二部分:表示被迭代的范围
0x01 范围 for 的用法
💬 使用方法演示:
int main() { int arr[] = { 1, 2, 3, 4, 5 }; for (auto e : arr) { printf("%d ", e); } printf("\n"); return 0; }
🚩 运行结果如下:
💬 试着使用范围 for,把数组中的每个值 +1 (1 2 3 4 5 → 2 3 4 5 6)
#include<iostream> using namespace std; int main() { int arr[] = { 1, 2, 3, 4, 5 }; // ++ for (auto& e : arr) { e++; } // 打印 for (auto e : arr) { cout << e << " "; } cout << endl; return 0; }
🚩 运行结果如下:
🔑 解读:使用引用来对数组中的每个值进行修改。
(这里的 & 不是取地址,是 "引用" 。如果你不知道,可以看完下一章,再回来看)
📌 注意事项:和普通循环类似,可以用 continue 来结束本次循环,也可以用 break 来跳出整个循环。
0x01 范围 for 的使用条件
for 循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;
对于类而言,应该提供 begin 和 end 的方法,begin 和 end 就是 for 循环迭代的范围。
❌ 错误演示:下面的代码就是 for 循环范围不确定!
void TestFor(int arr[]) { for (auto& e : arr) { cout << e << endl; } }
🔑 这里传递过来的是数组的首元素地址,并不是数组,它会不知道范围是多少,所以会报错。
迭代的对象要实现 ++ 和 == 的操作
(关于迭代器这个问题,后期会讲,现在了解一下留个印象即可)
Ⅳ. 指针空值 nullptr
0x00 C++ 98 中的指针空值
在良好的 C/C++ 变成习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。
如果一个指针没有合法的指向,我们就需要手动给它置为空。
在之前的C语言教程里,我们都是用 NULL 来解决的:
#include<iostream> using namespace std; int main(void) { // C++ 98/03 int* p1 = NULL; int* p2 = 0; return 0; }
🔑 这里的 NULL 其实是一个宏。
📚 C++ 空指针推荐使用 nullptr 来处理
int* p3 = nullptr;
这是 C++11 新增的关键字,以后就不再推荐使用 NULL 了。
📌 注意事项
① 使用 nullptr 表示指针空值时,因为它是关键字,所以不需要包含头文件。
② C++11 中,sizeof( nullptr ) 与 sizeof( (void*)0 ) 所占的字节数相同。
③ 为了提高代码的健壮性,在后续表示指针空值的时建议最好使用 nullptr 。
0x01 引入 nullptr 的原因
正如之前所说,NULL 其实是一个宏:
我们打开传统的 C 头文件 (stddef.h) 中可以看到如下代码:
#ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif
可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针 (void*) 的常量。
不论采取何种定义,在使用空值和指针时,都不可避免地会遇到一些麻烦,比如:
void Func(int) { cout << "Func(int)" << endl; } void Func(int*) { cout << "Func(int*)" << endl; } int main() { Func(0); Func(NULL); Func((int*)NULL); return 0; }
🔑 该程序的本意是想通过 Func(NULL) 调用指针版本的 Func(int*) 函数,但是由于 NULL 被定义成0,这么一来就不符合程序的初衷了。
在 C++98 中,字面常量 0 既可以是一个整型数字,也可以是无类型的指针 (void*) 常量,但是编译器默认情况下会将其看成一个整型常量,如果要将其按照指针方式来使用,必须对其进行强制类型转换 (void*)0 。
后来C++11引入了指针空值 nullptr 就缓解了这一尴尬现象。
🔺 总结:nullptr 其实就是 0,所以有了 C++11之后,就不再推荐大家使用 NULL 了。