[页眉: 15. 基于范围的 for 循环]
1) 语法格式:
①语法格式:[i:语法格式为][b:
```c++
for (decl : coll) {
statement
}
// decl 即符号声明(如 int n、auto a、const std::string& str、auto&& r 等);
// 声明的符号代表集合中的每个元素的拷贝(当使用非引用类型或自动推导为非引用类型时)或引用/本身(使用引用类型或自动推导为引用类型时);
// coll 代表一个集合,可以是数组、STL 容器、支持迭代器的自定义容器;
// statement 代表对每个元素的操作;
```
]
②使用值传递/左值引用/常左值引用/右值引用/通用引用的对应情况:[b:
a.小对象类型、不需要修改元素 => 值传递;
b.大对象类型、不需要修改元素 => 常左值引用;
c.需要修改元素、左值集合 => 左值/通用引用;
d.需要修改元素、右值集合 => 右值/通用引用;
]
③decl 中的类型是否必须与元素类型一致:[i:可以不一致,但是元素类型必须能隐式转换为 decl 中指定的类型;「i:例如以下代码就会编译失败」][b:「b:
```c++
class C {
...
// 可以将 std::string 转换为 C 类型、但禁止隐式类型转换
explicit C(const std::string& str) {
...
}
...
};
// 元素类型为 std::string 类型的 vector 容器
std::vector<std::string> vec;
// 理论上 decl 中的类型应该用 std::string,但是这里用了 C,而且 std::string 不能隐式转换为 C,因此以下代码编译失败
for (const C& elem : vec) {
...
}
```
」]
2) 本质:[b:
```c++
// 若 coll 支持内部迭代器
for (auto _pos = coll.begin(); _pos != coll.end(); ++_pos) {
decl = *_pos;
statement
}
// 若 coll 仅支持外部迭代器
for (auto _pos = begin(coll); _pos != end(coll); ++_pos) {
// 注意 begin() 和 end() 无作用域限定符,会触发普通查找和实参依赖查找(ADL)
decl = *_pos;
statement
}
```
对于第二种情况,begin() 和 end() 都是无作用域限定的函数调用,会触发普通查找和实参依赖查找;因此对于 STL 容器而言,调用的其实是 std 命名空间中的 std::beign() 和 std::end(),它们都是 C++ 20 引入的函数;「i:例如」「b:
```c++
template <typename T>
void foo(const T& coll) {
for (const auto& e : coll) {
...
}
}
// 若 T 存在内部迭代器,则其本质为
template <typename T>
void foo(const T& coll) {
for (auto _pos = coll.begin(); _pos != coll.end(); ++_pos) {
const auto& e = *_pos;
...
}
}
```
」
]
3) 自定义容器支持基于范围 for 循环:[b:
a.对于元素连续存储的容器,提供内部或外部的返回首元素的 begin()、返回尾元素的下一个位置的 end();
b.对于元素非连续存储的容器,提供内部或外部的返回首元素迭代器的 begin()、返回尾元素的下一个位置的迭代器的 end(),并且其迭代器要重载不等于、自增和解引用运算符;「i:注意元素非连续存储的容器要想使用基于范围的 for 循环、begin() 和 end() 只能返回迭代器而不是元素或节点指针,因为元素或节点指针的自增并不会得到下一个元素或节点的地址,而使用迭代器可以重载自增运算符,让对迭代器自增后能正确指向下一个元素或节点;」
「b: 例如
```c++
// 演示元素连续存储时提供内部 begin/end() 方法
#include <iostream>
#include <cstring>
class SqList {
private:
int* __base;
std::size_t __length;
std::size_t __size;
public:
// 默认构造:构造一个空的顺序表
SqList() :
__base(nullptr),
__length(0),
__size(0)
{}
// 从数组构造
SqList(const int* arr, std::size_t length) :
__base(new int[length]{0}),
__length(length),
__size(length * sizeof(int))
{
::memcpy(__base, arr, length * sizeof(int));
}
// 支持列表初始化
SqList(std::initializer_list<int> init_list) :
__base(new int[init_list.size()]{0}),
__length(init_list.size()),
__size(init_list.size() * sizeof(int))
{
std::size_t index = 0;
for (auto _pos = init_list.begin(); _pos != init_list.end(); ++_pos, ++index) {
__base[index] = *_pos;
}
}
// 析构:销毁顺序表
~SqList() {
delete[] __base;
__base = nullptr;
__length = 0;
__size = 0;
}
// 根据索引获取元素(无需检查 index 是否越界,因为若越界则是未定义行为、与一般数组一致)
int operator[](std::size_t index) { return __base[index]; }
int operator[](std::size_t index) const { return __base[index]; }
// 获取长度
std::size_t get_length() { return __length; }
std::size_t get_length() const { return __length; }
// ...(其他方法,如清空、判空、插入删除元素、排序等)
// ----------------------------------
// 注意 end() 返回尾元素的后一个位置
int* begin() { return __base; }
const int* begin() const { return __base; }
int* end() { return __base + __length; }
const int* end() const { return __base + __length; }
//-----------------------------------
};
int main(int argc, char** argv) {
int arr[3] = {1, 2, 3};
SqList list1(arr, 3);
SqList list2 = {4, 5, 6};
// 传统 for 循环
for (std::size_t i = 0; i < list1.get_length(); ++i) {
std::cout << list1[i] << std::endl;
}
// 使用内部 begin() 和 end() 的 for 循环
for (int* p = list1.begin(); p != list1.end(); ++p) {
std::cout << *p << std::endl;
}
// 基于范围的 for 循环
for (int elem : list2) {
std::cout << elem << std::endl;
}
return 0;
}
```
```c++
// 演示元素连续存储时提供外部 begin() 和 end() 的情况
#include <iostream>
#include <cstring>
namespace ns {
class SqList {
private:
int* __base;
std::size_t __length;
std::size_t __size;
public:
// 默认构造:构造一个空的顺序表
SqList() :
__base(nullptr),
__length(0),
__size(0)
{}
// 从数组构造
SqList(const int* arr, std::size_t length) :
__base(new int[length]{0}),
__length(length),
__size(length * sizeof(int))
{
::memcpy(__base, arr, length * sizeof(int));
}
// 支持列表初始化
SqList(std::initializer_list<int> init_list) :
__base(new int[init_list.size()]{0}),
__length(init_list.size()),
__size(init_list.size() * sizeof(int))
{
std::size_t index = 0;
for (auto _pos = init_list.begin(); _pos != init_list.end(); ++_pos, ++index) {
__base[index] = *_pos;
}
}
// 析构:销毁顺序表
~SqList() {
delete[] __base;
__base = nullptr;
__length = 0;
__size = 0;
}
// 根据索引获取元素(无需检查 index 是否越界,因为若越界则是未定义行为、与一般数组一致)
int operator[](std::size_t index) { return __base[index]; }
int operator[](std::size_t index) const { return __base[index]; }
// 获取长度
std::size_t get_length() { return __length; }
std::size_t get_length() const { return __length; }
// ...(其他方法,如清空、判空、插入删除元素、排序等)
// begin() 和 end() 的友元声明
friend int* begin(SqList& list);
friend const int* begin(const SqList& list);
friend int* end(SqList& list);
friend const int* end(const SqList& list);
};
// ----------------------------------
// 注意 end() 返回尾元素的后一个位置
int* begin(SqList& list) { return list.__base; }
const int* begin(const SqList& list) { return list.__base; }
int* end(SqList& list) { return list.__base + list.__length; }
const int* end(const SqList& list) { return list.__base + list.__length; }
//-----------------------------------
}
int main(int argc, char** argv) {
int arr[3] = {1, 2, 3};
ns::SqList list1(arr, 3);
ns::SqList list2 = {4, 5, 6};
// 传统 for 循环
for (std::size_t i = 0; i < list1.get_length(); ++i) {
std::cout << list1[i] << std::endl;
}
// 使用外部 begin() 和 end() 的 for 循环(触发 ADL 查找)
for (int* p = begin(list1); p != end(list1); ++p) {
std::cout << *p << std::endl;
}
// 基于范围的 for 循环
for (int elem : list2) {
std::cout << elem << std::endl;
}
return 0;
}
```
```c++
// 演示元素非连续存储时提供内部或外部的 begin()、end() 函数和其迭代器的重载自增、解引用、不等于运算符的函数
#include <iostream>
// 单链表
class LinkList {
private:
// 节点类型
struct Node {
int __data;
Node* __next;
Node() :
__data(0),
__next(nullptr)
{}
Node(int data, Node* next) :
__data(data),
__next(next)
{}
};
private:
Node* __base; // 头指针
std::size_t __length; // 长度
public:
LinkList() :
__base(new Node(0, nullptr)), // 创建头结点
__length(0)
{}
// 让链表支持从数组初始化
/*
// 问题原因:使用 new T[] 分配的内存只能用 delete[] 释放
LinkList(const int* arr, std::size_t length) :
__base(length > 0 ?
new Node[length + 1] :
new Node(0, nullptr)
),
__length(length)
{
// 初始化头结点
__base[0].__data = 0;
__base[0].__next = __length > 0 ? &__base[1] : nullptr;
// 初始化各数据节点
for (std::size_t i = 0; i < length; ++i)
{
__base[i + 1].__data = arr[i];
__base[i + 1].__next = i + 1 < __length ? &__base[i + 2] : nullptr;
}
}
*/
LinkList(const int* arr, std::size_t length) :
__base(new Node),
__length(length)
{
Node* p = __base;
for (std::size_t i = 0; i < length; i++) {
p->__next = new Node(arr[i], nullptr);
p = p->__next;
}
}
// 让链表支持列表初始化
/*
// 问题原因:使用 new T[] 分配的内存只能用 delete[] 释放
LinkList(std::initializer_list<int> init_list) :
__base(init_list.size() > 0 ?
new Node[init_list.size() + 1] :
new Node(0, nullptr)
),
__length(init_list.size())
{
// 初始化头结点
__base[0].__data = 0;
__base[0].__next = __length > 0 ? &__base[1] : nullptr;
// 初始化各数据节点
std::size_t index = 1U;
for (auto _pos = init_list.begin(); _pos != init_list.end(); ++_pos, ++index)
{
__base[index].__data = *_pos;
__base[index].__next = index < __length ? &__base[index + 1] : nullptr;
}
}
*/
LinkList(std::initializer_list<int> init_list) :
__base(new Node),
__length(init_list.size())
{
Node* p = __base;
for (auto _pos = init_list.begin(); _pos != init_list.end(); ++_pos) {
p->__next = new Node(*_pos, nullptr);
p = p->__next;
}
}
~LinkList()
{
Node* p = __base;
Node* p_next = __base->__next;
for(;;) {
delete p;
p = p_next;
if (!p_next) break;
p_next = p_next->__next;
}
__base = nullptr;
__length = 0;
}
public:
// (单向)迭代器
class Iterator {
friend class LinkList;
private:
Node* __p;
private:
Iterator() {}
public:
// 前自增(先自增后反回其本身)
Iterator& operator++() {
__p = __p->__next;
return *this;
}
// 后自增(自增后返回自增前的副本)
Iterator operator++(int) {
Iterator tmp = *this;
__p = __p->__next;
return tmp;
}
// 解引用
int operator*() {
return __p->__data;
}
int operator*() const {
return __p->__data;
}
// 不相等
bool operator!=(const Iterator& other) {
return other.__p != this->__p;
}
bool operator!=(const Iterator& other) const {
return other.__p != this->__p;
}
// ...(其他方法)
};
public:
// 能返回首尾元素迭代器的 begin() 和 end() 函数
Iterator begin() {
Iterator _pos;
_pos.__p = __base->__next;
return _pos;
}
const Iterator begin() const {
Iterator _pos;
_pos.__p = __base->__next;
return _pos;
}
Iterator end() {
/*
// 问题原因:标准库中 end() 返回尾元素的下一个位置,因此对于单链表和单向迭代器可直接返回 nullptr
Iterator _pos;
Node* p = __base;
while (p->__next) {
p = p->__next;
}
_pos.__p = p->__next;
return _pos;
*/
Iterator _pos;
_pos.__p = nullptr;
return _pos;
}
const Iterator end() const {
/*
// 问题原因:标准库中 end() 返回尾元素的下一个位置,因此对于单链表和单向迭代器可直接返回 nullptr
Iterator _pos;
Node* p = __base;
while (p->__next) {
p = p->__next;
}
_pos.__p = p->__next;
return _pos;
*/
Iterator _pos;
_pos.__p = nullptr;
return _pos;
}
};
int main(int argc, char** argv) {
int arr[3] = {1, 2, 3};
LinkList list1(arr, 3);
LinkList list2 = {4, 5, 6};
for (LinkList::Iterator _pos = list1.begin(); _pos != list1.end(); ++_pos) {
std::cout << *_pos << std::endl;
}
for (int elem : list2) {
std::cout << elem << std::endl;
}
return 0;
}
```
」
]