以下面两行代码为例:
for (int i = 0; i < some_vector.size(); i++)
{
//do stuff
}
这:
for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
some_iterator++)
{
//do stuff
}
有人告诉我第二种方法更可取。为什么会这样呢?
以下面两行代码为例:
for (int i = 0; i < some_vector.size(); i++)
{
//do stuff
}
这:
for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
some_iterator++)
{
//do stuff
}
有人告诉我第二种方法更可取。为什么会这样呢?
当前回答
因为您没有将代码绑定到some_vector列表的特定实现。如果你使用数组下标,它必须是某种形式的数组;如果使用迭代器,则可以在任何列表实现上使用该代码。
其他回答
我觉得这里的答案没有一个能解释为什么我喜欢把迭代器作为一个通用概念,而不是索引到容器中。请注意,我使用迭代器的大部分经验实际上并不是来自c++,而是来自Python等高级编程语言。
迭代器接口对函数的使用者施加的要求更少,这允许使用者使用它做更多的事情。
如果你所需要的只是能够进行前向迭代,那么开发人员就不局限于使用可索引容器——他们可以使用任何实现运算符++(T&)、运算符*(T)和运算符!=(const &T, const &T)。
#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
for (auto current = begin; current != end; ++current) {
std::cout << *current << "\n";
}
}
// elsewhere...
printAll(myVector.begin(), myVector.end());
你的算法适用于你需要的情况-迭代一个向量-但它也可以用于你不一定预期的应用程序:
#include <random>
class RandomIterator
{
private:
std::mt19937 random;
std::uint_fast32_t current;
std::uint_fast32_t floor;
std::uint_fast32_t ceil;
public:
RandomIterator(
std::uint_fast32_t floor = 0,
std::uint_fast32_t ceil = UINT_FAST32_MAX,
std::uint_fast32_t seed = std::mt19937::default_seed
) :
floor(floor),
ceil(ceil)
{
random.seed(seed);
++(*this);
}
RandomIterator& operator++()
{
current = floor + (random() % (ceil - floor));
}
std::uint_fast32_t operator*() const
{
return current;
}
bool operator!=(const RandomIterator &that) const
{
return current != that.current;
}
};
int main()
{
// roll a 1d6 until we get a 6 and print the results
RandomIterator firstRandom(1, 7, std::random_device()());
RandomIterator secondRandom(6, 7);
printAll(firstRandom, secondRandom);
return 0;
}
试图实现一个方括号操作符来做类似于这个迭代器的事情是不合理的,而迭代器的实现相对简单。方括号操作符还暗示了类的功能——可以将其索引到任意点——实现起来可能比较困难或效率较低。
迭代器也用于修饰。人们可以编写迭代器,在其构造函数中接受迭代器并扩展其功能:
template<class InputIterator, typename T>
class FilterIterator
{
private:
InputIterator internalIterator;
public:
FilterIterator(const InputIterator &iterator):
internalIterator(iterator)
{
}
virtual bool condition(T) = 0;
FilterIterator<InputIterator, T>& operator++()
{
do {
++(internalIterator);
} while (!condition(*internalIterator));
return *this;
}
T operator*()
{
// Needed for the first result
if (!condition(*internalIterator))
++(*this);
return *internalIterator;
}
virtual bool operator!=(const FilterIterator& that) const
{
return internalIterator != that.internalIterator;
}
};
template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
EvenIterator(const InputIterator &internalIterator) :
FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
{
}
bool condition(std::uint_fast32_t n)
{
return !(n % 2);
}
};
int main()
{
// Rolls a d20 until a 20 is rolled and discards odd rolls
EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
printAll(firstRandom, secondRandom);
return 0;
}
虽然这些玩具看起来很普通,但不难想象使用迭代器和迭代器装饰器在一个简单的接口上做强大的事情——例如,用一个从单个结果构造模型对象的迭代器装饰数据库结果的仅向前迭代器。这些模式使无限集的内存高效迭代成为可能,并且,使用像我上面写的过滤器,可能会延迟结果的计算。
c++模板的部分强大之处在于你的迭代器接口,当应用于固定长度的C数组时,它会退化为简单高效的指针算术,使其成为真正的零成本抽象。
第二种形式更准确地表示您正在做什么。在你的例子中,你并不关心i的值,你所需要的只是迭代器中的下一个元素。
如果要在迭代vector时向其添加/删除项,则可能需要使用迭代器。
some_iterator = some_vector.begin();
while (some_iterator != some_vector.end())
{
if (/* some condition */)
{
some_iterator = some_vector.erase(some_iterator);
// some_iterator now positioned at the element after the deleted element
}
else
{
if (/* some other condition */)
{
some_iterator = some_vector.insert(some_iterator, some_new_value);
// some_iterator now positioned at new element
}
++some_iterator;
}
}
如果使用索引,则必须在数组中上下移动项以处理插入和删除。
我不使用迭代器的原因与我不喜欢foreach-statements的原因相同。当有多个内部循环时,如果不记住所有的局部值和迭代器名称,就很难跟踪全局/成员变量。我发现有用的是在不同的情况下使用两组指标:
for(int i=0;i<anims.size();i++)
for(int j=0;j<bones.size();j++)
{
int animIndex = i;
int boneIndex = j;
// in relatively short code I use indices i and j
... animation_matrices[i][j] ...
// in long and complicated code I use indices animIndex and boneIndex
... animation_matrices[animIndex][boneIndex] ...
}
例如,我甚至不想将“animation_matrices[I]”缩写为一些随机的“anim_matrix”-name -iterator,因为这样你就不能清楚地看到这个值来自哪个数组。
如果你喜欢接近金属/不相信它们的实现细节,就不要使用迭代器。 如果在开发过程中经常从一种集合类型切换到另一种集合类型,请使用迭代器。 如果您发现很难记住如何迭代不同类型的集合(可能您使用了来自几个不同外部源的几种类型),请使用迭代器来统一遍历元素的方法。这适用于切换一个链表和一个数组列表。
真的,就是这样。这并不是说您要获得更多的简洁,如果简洁确实是您的目标,您总是可以求助于宏。