0%

CPP foreach循环浅析

概况

CPP foreach循环是CPP11引入的新特性,其能够很轻易地完成对可迭代对象的遍历,一般而言,一个典型的foreach循环结构如下:

1
2
3
for (const auto &item : contain) {
do_something(item);
}

可迭代对象

可迭代对象(上述示例中的contain)是一个可以使用迭代器遍历的对象,其要求满足以下条件:

  1. 实现了begin方法,并要求该方法返回一个迭代器且无参数,一个典型的声明如下:

    iterator begin() const;

  2. 实现了end方法,并要求该方法返回一个迭代器且无参数,一个典型的声明如下:

    iterator end() const;

而迭代器要求满足以下条件:

  1. 实现了相应的构造函数和赋值函数。

  2. 实现了operator++方法,一个典型的声明如下:

    iterator &operator++();

  3. 实现了operator!=方法,一个典型的声明如下:

    bool operator!=(const iterator &) const;

  4. 实现了operator*方法,一个典型的声明如下:

    T &operator*() const;

等价代码

实际上CPP中的foreach循环是一种语法糖,其等价于如下代码:

1
2
3
4
5
6
7
8
{
iterator first = contain.begin();
iterator last = contain.end();
while (first != last) {
do_something(*first);
++first;
}
}

代码示例

下面展示了一个对自定义可迭代对象进行迭代的Demo示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>

class Range {
public:
class RangeIterator {
public:
explicit RangeIterator(std::size_t i) :m_number_(i) {
std::cout << __FILE__ << ':' << __LINE__ << " ictor" << std::endl;
}

RangeIterator(const RangeIterator &it) :RangeIterator(it.m_number_) {
std::cout << __FILE__ << ':' << __LINE__ << " icopy_ctor" << std::endl;
}

RangeIterator(RangeIterator &&it) :RangeIterator(it.m_number_) {
std::cout << __FILE__ << ':' << __LINE__ << " imove_ctor" << std::endl;
}

RangeIterator &operator=(const RangeIterator &it) {
m_number_ = it.m_number_;
std::cout << __FILE__ << ':' << __LINE__ << " icopy" << std::endl;
return *this;
}

RangeIterator &operator=(RangeIterator &&it) {
m_number_ = it.m_number_;
std::cout << __FILE__ << ':' << __LINE__ << " imove" << std::endl;
return *this;
}

bool operator!=(const RangeIterator &rit) const {
std::cout << __FILE__ << ':' << __LINE__ << " operator!=" << std::endl;
return m_number_ != rit.m_number_;
}

RangeIterator &operator++() {
std::cout << __FILE__ << ':' << __LINE__ << " operator++" << std::endl;
++m_number_;
return *this;
}

std::size_t operator*() const {
std::cout << __FILE__ << ':' << __LINE__ << " *" << std::endl;
return m_number_;
}
private:
std::size_t m_number_;
};
public:
explicit Range(std::size_t first = 0, std::size_t last = SIZE_MAX) :
m_first_(first), m_last_(last) {
std::cout << __FILE__ << ':' << __LINE__ << " ctor" << std::endl;
}

Range(const Range &range) :
m_first_(range.m_first_), m_last_(range.m_last_) {
std::cout << __FILE__ << ':' << __LINE__ << " copy_ctor" << std::endl;
}

Range(Range &&range) :
m_first_(std::move(range.m_first_)), m_last_(std::move(range.m_last_)) {
std::cout << __FILE__ << ':' << __LINE__ << " move_ctor" << std::endl;
}

Range &operator=(const Range &range) {
m_first_ = range.m_first_;
m_last_ = range.m_last_;
std::cout << __FILE__ << ':' << __LINE__ << " copy" << std::endl;
return *this;
}

Range &operator=(Range &&range) {
m_first_ = std::move(range.m_first_);
m_last_ = std::move(range.m_last_);
std::cout << __FILE__ << ':' << __LINE__ << " move_ctor" << std::endl;
return *this;
}

RangeIterator begin() const {
std::cout << __FILE__ << ':' << __LINE__ << " begin" << std::endl;
return m_first_;
}

RangeIterator end() const {
std::cout << __FILE__ << ':' << __LINE__ << " end" << std::endl;
return m_last_;
}
private:
RangeIterator m_first_;
RangeIterator m_last_;
};

int main() {
for (const auto &i : Range(0, 4)) {
std::cout << __FILE__ << ':' << __LINE__ << ' ' << i << std::endl;
}
return 0;
}

编译运行后的结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
test0.cpp:8 ictor
test0.cpp:8 ictor
test0.cpp:52 ctor
test0.cpp:80 begin
test0.cpp:8 ictor
test0.cpp:12 icopy_ctor
test0.cpp:85 end
test0.cpp:8 ictor
test0.cpp:12 icopy_ctor
test0.cpp:32 operator!=
test0.cpp:43 *
test0.cpp:95 0
test0.cpp:37 operator++
test0.cpp:32 operator!=
test0.cpp:43 *
test0.cpp:95 1
test0.cpp:37 operator++
test0.cpp:32 operator!=
test0.cpp:43 *
test0.cpp:95 2
test0.cpp:37 operator++
test0.cpp:32 operator!=
test0.cpp:43 *
test0.cpp:95 3
test0.cpp:37 operator++
test0.cpp:32 operator!=