Cod*_*asy 100 c++ indexing iterator loops c++11
可能重复:
为什么使用迭代器而不是数组索引?
我正在回顾我对C++的了解,我偶然发现了迭代器.我想知道的一件事是什么让它们如此特别,我想知道为什么:
using namespace std;
vector<int> myIntVector;
vector<int>::iterator myIntVectorIterator;
// Add some elements to myIntVector
myIntVector.push_back(1);
myIntVector.push_back(4);
myIntVector.push_back(8);
for(myIntVectorIterator = myIntVector.begin();
myIntVectorIterator != myIntVector.end();
myIntVectorIterator++)
{
cout<<*myIntVectorIterator<<" ";
//Should output 1 4 8
}
Run Code Online (Sandbox Code Playgroud)
比这更好:
using namespace std;
vector<int> myIntVector;
// Add some elements to myIntVector
myIntVector.push_back(1);
myIntVector.push_back(4);
myIntVector.push_back(8);
for(int y=0; y<myIntVector.size(); y++)
{
cout<<myIntVector[y]<<" ";
//Should output 1 4 8
}
Run Code Online (Sandbox Code Playgroud)
是的,我知道我不应该使用std命名空间.我刚把这个例子从cprogramming网站上删除了.那么请你告诉我为什么后者更糟?有什么大不同?
Tem*_*Rex 171
迭代器的特殊之处在于它们提供了算法和容器之间的粘合剂.对于通用代码,该建议是使用(例如STL算法的组合find
,sort
,remove
,copy
)等,其执行的是你心里有你的数据结构(计算vector
,list
,map
等),并提供与算法迭代器进入容器.
您的特定示例可以写为for_each
算法和vector
容器的组合(请参阅下面的选项3),但它只是四种不同的方法来迭代std :: vector:
1)基于索引的迭代
for (std::size_t i = 0; i != v.size(); ++i) {
// access element as v[i]
// any code including continue, break, return
}
Run Code Online (Sandbox Code Playgroud)
优点:熟悉C风格代码的任何人都熟悉,可以使用不同的步幅循环(例如i += 2
).
缺点:只为顺序随机访问容器(vector
,array
,deque
),不适合工作list
,forward_list
或关联容器.循环控制也有点冗长(init,check,increment).人们需要了解C++中基于0的索引.
2)基于迭代器的迭代
for (auto it = v.begin(); it != v.end(); ++it) {
// if the current index is needed:
auto i = std::distance(v.begin(), it);
// access element as *it
// any code including continue, break, return
}
Run Code Online (Sandbox Code Playgroud)
优点:更通用,适用于所有容器(即使是新的无序关联容器,也可以使用不同的步幅(例如std::advance(it, 2)
);
缺点:需要额外的工作来获取当前元素的索引(可以是列表或forward_list的O(N)).同样,循环控制有点冗长(init,check,increment).
3)STL for_each算法+ lambda
std::for_each(v.begin(), v.end(), [](T const& elem) {
// if the current index is needed:
auto i = &elem - &v[0];
// cannot continue, break or return out of the loop
});
Run Code Online (Sandbox Code Playgroud)
优点:与2相同,加上环路控制的小幅减少(无检查和增量),这可以大大降低您的错误率(错误的初始化,检查或增加,逐个错误).
缺点:与显式迭代器循环相同,加上循环中流控制的限制可能性(不能使用continue,break或return),并且没有不同步幅的选项(除非使用重载的迭代器适配器operator++
).
4)range-for循环
for (auto& elem: v) {
// if the current index is needed:
auto i = &elem - &v[0];
// any code including continue, break, return
}
Run Code Online (Sandbox Code Playgroud)
优点:非常紧凑的循环控制,可直接访问当前元素.
缺点:获取索引的额外声明.不能使用不同的步伐.
用什么?
对于你迭代的特定例子std::vector
:如果你真的需要索引(例如访问上一个或下一个元素,在循环内打印/记录索引等)或者你需要一个不同于1的步幅,那么我会明确地去indexed-loop,否则我会选择range-for循环.
对于通用容器上的通用算法,我会选择显式迭代器循环,除非代码在循环中不包含流控制并且需要步长1,在这种情况下我会选择STL for_each
+ lambda.
迭代器使您的代码更通用.
每个标准库容器都提供一个迭代器,因此如果您将来更改容器类,则循环不会受到影响.
迭代器是首选operator[]
.C++ 11提供std::begin()
,std::end()
功能.
由于您的代码只使用std::vector
,我不能说两个代码有很大差异,但是,operator []
可能无法按照您的意图运行.例如,如果使用map,operator[]
则会在未找到时插入元素.
此外,通过使用iterator
您的代码在容器之间变得更加便携.如果使用迭代器,则可以自由地将容器从容器或其他容器切换std::vector
到std::list
其他容器而不会改变太多,这样的规则不适用operator[]
.
使用向量迭代器不会提供任何真正的优势.语法更加丑陋,键入的时间更长,难以阅读.
使用迭代器迭代向量并不快,并且不安全(实际上,如果使用迭代器在迭代期间可能调整大小,将会给您带来很大的麻烦).
在以后更改容器类型时使用通用循环的想法在实际情况下也大多是无意义的.不幸的是,没有严格打字推断的严格打字语言的黑暗面(现在用C++ 11好一点)是你需要说出每一步的所有内容的类型.如果你以后改变主意,你仍然需要四处走动并改变一切.此外,不同的容器具有非常不同的权衡,并且更换容器类型不是经常发生的事情.
如果可能的泛型应该保持迭代的唯一情况是在编写模板代码时,但是(我希望你)并不是最常见的情况.
显式索引循环中唯一存在的问题是size
返回无符号值(C++的设计错误),有符号和无符号之间的比较是危险和令人惊讶的,因此最好避免.如果你使用一个正常的编译器并启用了警告,那么就应该有一个诊断.
请注意,解决方案不是使用unsiged作为索引,因为无符号值之间的算术也显然是不合逻辑的(它是模运算,并且x-1
可能大于x
).相反,您应该在使用之前将大小转换为整数.它可以做一些有意义的使用无符号的大小和索引(支付的关注很多你写的每一个表情),只有当你在一个16位的C++实现的工作(16位是在大小为无符号值的原因).
作为无符号大小可能引入的典型错误考虑:
void drawPolyline(const std::vector<P2d>& points)
{
for (int i=0; i<points.size()-1; i++)
drawLine(points[i], points[i+1]);
}
Run Code Online (Sandbox Code Playgroud)
这里存在错误,因为如果你传递一个空points
向量,那么该值points.size()-1
将是一个巨大的正数,使你循环进入段错误.一个有效的解决方案
for (int i=1; i<points.size(); i++)
drawLine(points[i - 1], points[i]);
Run Code Online (Sandbox Code Playgroud)
但我个人更喜欢永远删除unsinged
-ness with int(v.size())
.
在这种情况下使用迭代器的丑陋留给读者练习.
归档时间: |
|
查看次数: |
158557 次 |
最近记录: |