在我的游戏引擎项目中,我广泛使用STL,主要是std::string
和std::vector
类。
在许多情况下,我必须遍历它们。现在,我这样做的方式是:
for( unsigned int i = 0; i < theContainer.size(); i ++ )
{
}
如果没有,为什么,我该怎么做呢?
使用此实现,每个循环周期是否真的执行了size()?性能损失是否可以忽略不计?
答案 0 :(得分:9)
C ++ 11有一个新的容器感知循环语法,如果你的编译器支持新标准,可以使用它。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> vs;
vs.push_back("One");
vs.push_back("Two");
vs.push_back("Three");
for (const auto &s : vs)
{
cout << s << endl;
}
return 0;
}
答案 1 :(得分:5)
您可能希望查看标准算法。
例如
vector<mylass> myvec;
// some code where you add elements to your vector
for_each(myvec.begin(), myvec.end(), do_something_with_a_vector_element);
其中do_something_with_a_vector_element
是一个执行循环内容的函数
例如
void
do_something_with_a_vector_element(const myclass& element)
{
// I use my element here
}
有许多标准算法 - 请参阅http://www.cplusplus.com/reference/algorithm/ - 所以大多数事情都得到支持
答案 2 :(得分:4)
STL容器支持迭代器
vector<int> v;
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
cout << *it << endl;
}
每次迭代都会重新计算size()。
答案 3 :(得分:2)
但您可以使用迭代器。
for (string::const_iterator it = theContainer.begin();
it != theContainer.end(); ++it) {
// do something with *it
}
在某些情况下,编译器可以优化.size()
(或迭代器案例中的.end()
)调用(例如,仅const
访问,函数为{{ 1}})。但不要依赖它。
答案 4 :(得分:1)
通常在容器上“迭代”的正确方法是使用“迭代器”。像
这样的东西string myStr = "hello";
for(string::iterator i = myStr.begin(); i != myStr.end(); ++i){
cout << "Current character: " << *i << endl;
}
当然,如果您不打算修改每个元素,最好使用string::const_iterator
。
是的,size()
每次被调用,并且它是O(n),因此在许多情况下性能损失将是显而易见的并且它是O(1),但它是一个在循环之前计算大小的好习惯,而不是每次调用大小。
答案 5 :(得分:1)
不,这不是正确的方法。对于::std::vector
或::std::string
,它可以正常工作,但问题是,如果您使用其他任何东西,它将无法正常工作。此外,它不是惯用的。
并且,为了回答你的另一个问题...... size
函数可能是内联的。这意味着它可能只是从::std::string
或::std::vector
的内部获取值。编译器将对此进行优化,并且在大多数情况下仅获取一次。
但是,这是惯用的方式:
for (::std::vector<Foo>::iterator i = theContainer.begin();
i != theContainer.end();
++i)
{
Foo &cur_element = *i;
// Do stuff
}
++i
非常重要。同样,对于::std:vector
或::std::string
,迭代器基本上是一个指针,它并不那么重要。但对于更复杂的数据结构而言。 i++
必须制作副本并创建一个临时副本,因为旧值需要坚持下去。 ++i
没有这样的问题。养成总是使用++i
的习惯,除非你有令人信服的理由不这样做。
最后,theContainer.end()
通常也会被优化。但是你可以通过这样做来强迫事情变得更好:
const ::std::vector<Foo>::iterator theEnd = theContainer.end();
for (::std::vector<Foo>::iterator i = theContainer.begin(); i != theEnd; ++i)
{
Foo &cur_element = *i;
// Do stuff
}
当然,C ++ 0x通过for
循环的新语法大大简化了所有这些:
for (Foo &i: theContainer)
{
// Do stuff with i
}
这些将适用于标准的修复大小的数组以及定义begin
和end
以返回类似迭代器的任何类型。
答案 6 :(得分:1)
原生for循环(尤其是基于索引) - 它是C-way,而不是C ++方式。
使用BOOST_FOREACH进行循环。
比较,对于整数容器:
typedef theContainer::const_iterator It;
for( It it = theContainer.begin(); it != theContainer.end(); ++it ) {
std::cout << *it << std::endl;
}
和
BOOST_FOREACH ( int i, theContainer ) {
std::cout << i << std::endl;
}
但这不是完美的方式。如果你可以在没有循环的情况下完成工作 - 你必须在没有循环的情况下完成。例如,使用算法和Boost.Phoenix:
boost::range::for_each( theContainer, std::cout << arg1 << std::endl );
我知道这些解决方案会在代码中带来额外的依赖关系,但Boost是现代C ++的“必备”。
答案 7 :(得分:0)
你对矢量没有问题,虽然这并没有转化为其他容器的正确方法。
更通用的方法是
for(std::vector<foo>::const_iterator i = theContainer.begin(); i != theContainer.end; ++i)
这比我真正喜欢的打字更多,但在即将推出的标准中重新定义auto
会变得更加合理。这适用于所有标准容器。请注意,您将个人foo
称为*i
,如果您想要其地址,请使用&*i
。
在循环中,每次都会执行.size()
。但是,对于所有标准容器来说,它是恒定时间(标准,23.1 / 5),因此如果有的话,它不会减慢你的速度。另外:标准说“应该”具有不变的复杂性,因此特别糟糕的实现可能使其不稳定。如果你使用这么糟糕的实现,你还需要担心其他性能问题。