对于std::list,最基本的遍历方式可能是这样的:
list<int> l = { 0,1,2,3,4 }; for (list<int>::iterator it = l.begin(); it != l.end(); i++) { cout << *it << endl; }
std::list的iterator内部维护着指向内部节点的指针,begin和end函数返回对应节点的iterator。
iterator begin() noexcept { // return iterator for beginning of mutable sequence return (iterator(this->_Myhead()->_Next, _STD addressof(this->_Get_data()))); }
iterator实现了对应节点的++、--,*等运算符,维护这个for循环。
_List_const_iterator operator--(int) { // postdecrement _List_const_iterator _Tmp = *this; --*this; return (_Tmp); } _List_const_iterator operator++(int) { // postincrement _List_const_iterator _Tmp = *this; ++*this; return (_Tmp); } _NODISCARD reference operator*() const { // return designated value return (this->_Ptr->_Myval); }
所以说在这个循环过程中,我们得到的迭代器其实是维护了一个指针的中间层对象,并不是内部节点对象。
而我们使用使用c++11的范围循环就不一样了,c++11的范围循环有以下两种用法:
for (auto &it : la) { //按引用循环 it.add(); } for (auto it : la) { //按值循环 cout << it.get() << endl; }
按值循环的方式与std::list迭代器所提供的遍历方式大相径庭,会将list的内部节点拷贝到循环变量it中,也就是说使用按值循环的方式修改的只是个临时变量,并没有修改list的内部节点:
int main() { list<int> l = { 0,1 }; for (auto it : l) { //正确的写法:for (auto &it : l) { it++; } for (auto it : l) { //在遍历时还会调用拷贝构造函数,增加开销 cout << it << endl; } getc(stdin); return 0; } //Output: //0 //1
这种范围的原理和algorithm中提供的for_each()类似:
template<class InputIterator, class Function> Function for_each(InputIterator first, InputIterator last, Function fn) { while (first!=last) { fn (*first); ++first; } return fn; // or, since C++11: return move(fn); }
for_each将内部节点的指针解引用,以右值的形式传递,如果我们不将function的参数类型变为引用,就无法修改内部节点。
int main() { list<int> l = { 0,1 }; for_each(l.begin(), l.end(), [](int x) -> void{ x++; }); for (auto it : l) { cout << it << endl; } // 0 1 for_each(l.begin(), l.end(), [](int &x) -> void { x++; }); for (auto it : l) { cout << it << endl; } // 1 2 getc(stdin); return 0; }