我有一个迭代的向量。在迭代时,我可能会向向量添加新值。它看起来像:
struct Foo
{
bool condition;
};
void AppendToVec(vector<Foo>& v)
{
...
v.push_back(...);
}
vector<Foo> vec;
...
for (vector<Foo>::size_type i = 0; i < vec.size(); ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
这很好用,实际上优雅地处理新附加元素递归需要添加更多元素的情况,但感觉有点脆弱。如果其他人出现并调整循环,它很容易被破坏。例如:
//No longer iterates over newly appended elements
vector<Foo>::size_type size = vec.size();
for (vector<Foo>::size_type i = 0; i < size; ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
或
//Vector resize may invalidate iterators
for (vector<Foo>::iterator i = vec.begin(); i != vec.end(); ++i)
{
if (vec->condition) AppendToVec(vec);
}
有没有最佳做法来处理这样的案件?用循环注释“警告:迭代时这个循环是故意附加到向量。谨慎改变”最好的方法?如果能让事情变得更加强大,我也愿意切换容器。
答案 0 :(得分:19)
我解决这个问题的方法通常是创建一个我添加任何新元素的队列,然后在迭代原始容器之后,处理队列中的元素和/或将它们附加到原始元素。
这种方法的优点是,正在发生的事情是显而易见的,并且它适用于多个线程可以将新元素排入队列的场景。
答案 1 :(得分:13)
如果其他人出现并调整循环,则很容易被破坏。
然后 不使用for
循环 ,而是使用while
循环。对我来说, 一个for
循环总是意味着使用计数器的简单迭代循环 。但是,如果我遇到while
循环,我觉得事情必须过于复杂,无法在简单的for
循环中表达它们。我会仔细观察,我对“优化”while
循环比使用for
循环更加谨慎。
答案 2 :(得分:5)
如果AppendToVec
已使用旧向量(i
)中的相对位置重新分配,则允许vec
更新i-vec.begin()
。
void AppendToVec(vector<int> & vec, vector<int>::iterator & i)
{
const int some_num = 1;
const size_t diff = i-vec.begin();
vec.push_back(some_num);
i = vec.begin()+diff;
}
int main(int argc, char* argv[])
{
const size_t arbit_size = 10;
const size_t prior_size = 3;
vector<int> vec;
// Fill it up with something
for(size_t i = 0; i < prior_size; ++i) vec.push_back(static_cast<int>(i));
// Iterate over appended elements
vector<int>::iterator i = vec.begin();
while(i != vec.end())
{
cout<<*i<<","<<vec.size()<<endl;
if(vec.size()<arbit_size) AppendToVec(vec,i);
++i;
}
return 0;
}
0,3
1,4
2,5
1,6
1,7
1,8
1,9
1,10
1,10
1,10
答案 3 :(得分:1)
正如已经指出的那样,正如您所感觉到的,这里存在一个问题。你有几种可能的解决方案,所以不要盲目收费。
WARNING
标签或任何编码标准要求的大量评论来警告未来的维护者,这涉及到一些诡计。最好不要使用,但可以工作。reserve
并完全阻止重新分配。std::deque
。大多数性能特征是相似的,但是你可以在不使迭代器/引用等失效的情况下添加和附加新值...看起来像这里自然适合我认为deque
是更好的解决方案。它适合您的算法,您不必担心这些问题。您可以用vector
替换代码中的大部分deque
。如果您不想更改界面:
vector
复制到deque
deque
内容分配到vector
vector
两次之外,不会涉及更多副本。所以请随意!
void treat(std::vector<int>& vec)
{
// WARNING: we use a deque because of its iterator invalidation properties
std::deque<int> deq(vec.begin(), vec.end());
for (std::deque<int>::const_iterator it = deq.begin(); it != deq.end(); ++it)
{
if (it->condition()) deq.push_back(func(*it));
}
vec.assign(deq.begin(), deq.end());
}
答案 4 :(得分:1)
内联评论通常足以使其明确,例如。
for (vector<Foo>::size_type i = 0; i < vec.size() /* size grows in loop! */; ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
答案 5 :(得分:0)
您是否需要随机访问此向量?如果不是,则std::list
更合适。我个人认为,在迭代过程中附加向量并不是一个好主意。
答案 6 :(得分:0)
您可以附加到双端队列而不会使其元素的迭代器失效。随机访问接近向量的效率,因此它通常是替换它的好选择。
答案 7 :(得分:-1)
与原版没有太大区别。只是在这里明确一些事情:
for (vector<Foo>::size_type i = 0, n = vec.size(); i < n; ++i)
if (vec[i].condition){
AppendToVec(vec);
n = vec.size();
}