2010-04-28 2 views
3

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

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  
+0

Это не причина ошибки, но вы должны использовать pre-increment для продвижения итераторов - ++ InterIterator. – markh44

+1

Не то, чтобы это действительно важно для производительности, оно оптимизировано большинством компиляторов. –

ответ

13

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

Ваш код должен выглядеть следующим образом:

if (condition) 
    interIterator = externIterator->erase(interIterator); 
else 
    ++interIterator; // (generally better practice to use pre-increment) 
+0

Спасибо, это очень помогло моей проблеме. –

5

Вы не можете удалять элементы из контейнера последовательности во время прохода по ней — по крайней мере, не так, как вы делаете это, потому что вызов — erase аннулированию итератора , Вы должны присвоить возвращаемое значение erase итератора и подавить приращение:

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

Кроме того, никогда не использовать пост-инкремент (я ++) при Преинкремент (++ я) будет делать.

1

Я возьму на себя смелость переписать код:

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

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

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

Смежные вопросы