在迭代时附加到向量?

时间:2010-08-09 19:21:42

标签: c++ stl

我有一个迭代的向量。在迭代时,我可能会向向量添加新值。它看起来像:

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);
}

有没有最佳做法来处理这样的案件?用循环注释“警告:迭代时这个循环是故意附加到向量。谨慎改变”最好的方法?如果能让事情变得更加强大,我也愿意切换容器。

8 个答案:

答案 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();
  }
相关问题