考虑以下两行代码:
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
}
我被告知第二种方式更受欢迎。为什么呢?
实际上,就是这样。无论哪种方式,在平均水平上都不会更加简洁,如果简洁确实是你的目标,你总是可以退而求其次使用宏。
已经有几个好观点了。我有一些额外的评论:
假设我们谈论的是C++标准库,“vector”意味着一个具有C数组保证(随机访问,连续内存布局等)的随机访问容器。如果你说“some_container”,那么上面的答案中很多都会更准确(容器独立性等)。
为了消除对编译器优化的任何依赖,您可以将一些_vector.size()移出索引代码中的循环,如下所示:
const size_t numElems = some_vector.size(); for (size_t i = 0; i
始终预增迭代器,并将后增操作视为异常情况。
因此,假设有一个可索引的std::vector<>
之类的容器,没有理由更喜欢顺序遍历容器中的一个而不是另一个。如果您需要频繁引用旧的或新的元素索引,则索引版本更合适。
我觉得这里的回答都没有解释为什么我喜欢迭代器作为一般概念,而不是索引容器。请注意,我的大部分使用迭代器的经验并不来自C++,而是来自像Python这样的高级编程语言。
迭代器接口对函数的消费者施加的要求较少,这使得消费者可以更多地使用它。
如果你只需要能够前向迭代,开发人员不必局限于使用可索引的容器 - 他们可以使用任何实现operator++(T&)
、operator*(T)
和operator!=(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 数组的数据结构时,会衰变为简单高效的指针算术运算,从而实现真正的零成本抽象。
为了实现容器独立性
这两种实现都是正确的,但我更喜欢使用'for'循环。因为我们决定使用Vector而不是其他容器,所以使用索引是最好的选择。使用迭代器与Vectors会失去将对象放在连续内存块中的好处,这有助于它们的访问。
我总是使用数组索引,因为我的许多应用程序需要类似“显示缩略图”的功能。所以我写了这样的代码:
some_vector[0].left=0;
some_vector[0].top =0;<br>
for (int i = 1; i < some_vector.size(); i++)
{
some_vector[i].left = some_vector[i-1].width + some_vector[i-1].left;
if(i % 6 ==0)
{
some_vector[i].top = some_vector[i].top.height + some_vector[i].top;
some_vector[i].left = 0;
}
}
some_iterator++
修改为++some_iterator
,则更倾向于使用第二种方式。因为后置自增会创建一个不必要的临时迭代器。 - jasonend()
加入声明子句。 - Lightness Races in Orbitvector::end
(向量的结尾) 的C++实现,可能存在比它是否被提取出循环更值得担心的问题。就我个人而言,我更喜欢清晰易懂的代码——如果终止条件中涉及到find
操作,那么我会感到有点担忧。 - Steve Jessopit != vec.end()
和it != end
,而是(vector<T>::iterator it = vec.begin(); it != vec.end(); ++it)
和(vector<T>::iterator it = vec.begin(), end = vec.end(); it != end; ++it)
。我不需要计算字符数。当然可以更喜欢其中一个,但别人对你偏好的异议并不是“粗心”,而是更喜欢变量更少、阅读时更易考虑的简化代码。 - Steve Jessop