Проблема итераторов C ++ - PullRequest
       15

Проблема итераторов C ++

3 голосов
/ 28 апреля 2010

Я работаю с итераторами на C ++, и у меня возникли некоторые проблемы здесь. В выражении (this -> _ Has_container ()) в строке interIterator ++ написано «Ошибка отладки подтверждения». Список расстояний - это вектор >. Что я делаю не так?

vector< vector<DistanceNode> >::iterator externIterator = distanceList.begin();

   while (externIterator != distanceList.end()) {

    vector<DistanceNode>::iterator interIterator = externIterator->begin();

        while (interIterator != externIterator->end()){

          if (interIterator->getReference() == tmp){

     //remove element pointed by interIterator
     externIterator->erase(interIterator);             

          } // if
    interIterator++;
  } // while
  externIterator++;
   } // while      

Ответы [ 3 ]

13 голосов
/ 28 апреля 2010

вектор erase() возвращает новый итератор для следующего элемента. Все итераторы стертого элемента и элементов после него становятся недействительными. Однако ваш цикл игнорирует это и продолжает использовать interIterator.

Ваш код должен выглядеть примерно так:

if (condition)
    interIterator = externIterator->erase(interIterator);
else
    ++interIterator;  // (generally better practice to use pre-increment)
5 голосов
/ 28 апреля 2010

Вы не можете удалять элементы из контейнера последовательности во время итерации по нему & mdash; по крайней мере, не так, как вы это делаете & mdash; потому что вызов erase делает итератор недействительным. Вы должны присвоить итератору возвращаемое значение из erase и подавить приращение:

while (interIterator != externIterator->end()){
   if (interIterator->getReference() == tmp){
       interIterator = externIterator->erase(interIterator);             
   } else {
       ++interIterator;
   }
}

Кроме того, никогда не используйте постинкремент (i ++), когда преинкремент (++ i) подойдет.

1 голос
/ 28 апреля 2010

Я позволю себе переписать код:

class ByReference: public std::unary_function<bool, DistanceNode>
{
public:
  explicit ByReference(const Reference& r): mReference(r) {}
  bool operator()(const DistanceNode& node) const
  {
    return node.getReference() == r;
  }
private:
  Reference mReference;
};

typedef std::vector< std::vector< DistanceNode > >::iterator iterator_t;

for (iterator_t it = dl.begin(), end = dl.end(); it != end; ++it)
{
  it->erase(
     std::remove_if(it->begin(), it->end(), ByReference(tmp)),
     it->end()
  );
}

Почему?

  • Первый цикл (externIterator) выполняет итерацию по всему диапазону элементов, никогда не изменяя сам диапазон, для этого и нужен for, так что вы не забудете увеличить его (предположительно, for_each будет быть лучше, но синтаксис может быть неудобно)
  • Второй цикл сложен: просто говоря, вы фактически обрезаете ветвь, на которой сидите, когда вызываете erase, что требует прыжков (используя возвращаемое значение). В этом случае операция, которую вы хотите выполнить (очистка списка в соответствии с определенными критериями), как раз и предназначена для идиомы remove-erase.

Обратите внимание, что код можно было бы привести в порядок, если бы в нашем распоряжении была настоящая лямбда-поддержка. В C ++ 0x мы бы написали:

std::for_each(distanceList.begin(), distanceList.end(),
  [const& tmp](std::vector<DistanceNode>& vec)
  {
    vec.erase(
      std::remove_if(vec.begin(), vec.end(),
        [const& tmp](const DistanceNode& dn) { return dn.getReference() == tmp; }
      ),
      vec.end()
    );
  }
);

Как вы можете видеть, мы не видим больше никакого увеличения / разыменования итератора, все это заключено в специальные алгоритмы, которые гарантируют, что все обрабатывается соответствующим образом.

Я предоставлю вам синтаксис, который выглядит странно, но я думаю, это потому, что мы еще не привыкли к нему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...