为什么 set(c++) 中的迭代器行为不正常?
Why is iterator in set(c++) not behaving properly?
这是我写的代码:
multiset<int>S;
for(int i = 0;i<20;i++)
S.insert(i);
for(auto it = S.end();it!=S.begin();--it)
cout<<*it<<" ";
cout<<endl;
输出:
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
更好用:
for(auto it = S.rbegin(); it != S.rend(); ++it)
根据评论更新
带有迭代器的循环不正确并且具有未定义的行为,因为成员函数 end() 返回的迭代器在循环中被取消引用。
一个有效的程序看起来像
#include <iostream>
#include <set>
int main()
{
std::multiset<int> s;
for ( int i = 0; i < 20; i++ ) s.insert( i );
for ( auto it = s.end(); it != s.begin(); ) std::cout << *--it << " ";
std::cout << std::endl;
return 0;
}
它的输出是
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
当然你可以使用函数rbegin()
返回的class的反向迭代器。例如
#include <iostream>
#include <set>
int main()
{
std::multiset<int> s;
for ( int i = 0; i < 20; i++ ) s.insert( i );
for ( auto it = s.rbegin(); it != s.rend(); ++it ) std::cout << *it << " ";
std::cout << std::endl;
return 0;
}
在这种情况下,循环看起来更简单。
您的代码包含一些未定义的行为。正如您已经指出的那样,S 将包含从 0 到 20(不含)的所有值,尽管打印以某种方式给出 1 到 20(含)。
您的代码:
for(auto it = S.end();it!=S.begin();--it)
cout<<*it<<" ";
这里的问题是范围 [begin, end)
有 end
指的是不属于集合的东西。取消引用从 end() 接收到的迭代器可能会使您的程序崩溃或让它产生一些随机值。在这种情况下,我猜你得到值 20 因为编译器优化。 (一些黑盒优化)
在C++(以及其他语言)中,迭代器的概念伴随着一个reverse iterators的概念。 (如果你遵循 link,有一张解释迭代器的漂亮图片。)
基本上,使用反向迭代器可以让您从头到尾循环,就像使用普通迭代器一样:
for (auto it = S.crbegin(); it != S.crend(); ++it)
cout << *it << " ";
请注意,rbegin() 和 crbegin() 在代码复杂性方面没有任何缺点。 (除非你想再次将它们转换为前向迭代器)
奖励:默认情况下,不要在迭代器上使用 -- 运算符,它会在尝试调试时让人头疼。
这是我写的代码:
multiset<int>S;
for(int i = 0;i<20;i++)
S.insert(i);
for(auto it = S.end();it!=S.begin();--it)
cout<<*it<<" ";
cout<<endl;
输出:
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
更好用:
for(auto it = S.rbegin(); it != S.rend(); ++it)
根据评论更新
带有迭代器的循环不正确并且具有未定义的行为,因为成员函数 end() 返回的迭代器在循环中被取消引用。
一个有效的程序看起来像
#include <iostream>
#include <set>
int main()
{
std::multiset<int> s;
for ( int i = 0; i < 20; i++ ) s.insert( i );
for ( auto it = s.end(); it != s.begin(); ) std::cout << *--it << " ";
std::cout << std::endl;
return 0;
}
它的输出是
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
当然你可以使用函数rbegin()
返回的class的反向迭代器。例如
#include <iostream>
#include <set>
int main()
{
std::multiset<int> s;
for ( int i = 0; i < 20; i++ ) s.insert( i );
for ( auto it = s.rbegin(); it != s.rend(); ++it ) std::cout << *it << " ";
std::cout << std::endl;
return 0;
}
在这种情况下,循环看起来更简单。
您的代码包含一些未定义的行为。正如您已经指出的那样,S 将包含从 0 到 20(不含)的所有值,尽管打印以某种方式给出 1 到 20(含)。
您的代码:
for(auto it = S.end();it!=S.begin();--it)
cout<<*it<<" ";
这里的问题是范围 [begin, end)
有 end
指的是不属于集合的东西。取消引用从 end() 接收到的迭代器可能会使您的程序崩溃或让它产生一些随机值。在这种情况下,我猜你得到值 20 因为编译器优化。 (一些黑盒优化)
在C++(以及其他语言)中,迭代器的概念伴随着一个reverse iterators的概念。 (如果你遵循 link,有一张解释迭代器的漂亮图片。)
基本上,使用反向迭代器可以让您从头到尾循环,就像使用普通迭代器一样:
for (auto it = S.crbegin(); it != S.crend(); ++it)
cout << *it << " ";
请注意,rbegin() 和 crbegin() 在代码复杂性方面没有任何缺点。 (除非你想再次将它们转换为前向迭代器)
奖励:默认情况下,不要在迭代器上使用 -- 运算符,它会在尝试调试时让人头疼。