2010-06-16 3 views
0
#include "stdafx.h" 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    string s = "Haven't got an idea why."; 
    auto beg = s.begin(); 
    auto end = s.end(); 
    while (beg < end) 
    { 
     cout << *beg << '\n'; 
     if (*beg == 'a') 
     {//whithout if construct it works perfectly 
      beg = s.erase(beg); 
     } 
     ++beg; 
    } 
    return 0; 
} 

Почему, если я удаляю один или несколько символов из этой строки, этот код прерывается? Я полагаю, что он имеет какое-то отношение к возвращенному итератору после операции стирания, создаваемой с более высоким адресом, чем конечный итератор, но я не уверен, и это, безусловно, неправильное поведение. Или это?поведение странного итератора

+0

Возможно, проще стереть символы из 'end()' в 'begin()', так как 'begin()' не будет меняться при стирании символов. – MSalters

ответ

8

С этим кодом связано несколько проблем.

  1. Не кэшируйте значение s.end(); он изменяется по мере удаления элементов.
  2. Не использовать beg < end. Идиоматический подход состоит в том, чтобы написать beg != end. Если вы попытаетесь пропустить минус end, результат будет неопределенным, а версия отладки библиотеки строк может привести к сбою вашего процесса, поэтому бессмысленно использовать <.
  3. Итератор возвращается с s.erase(beg) Возможно, s.end(), и в этом случае ++beg берет вас за конец.

Вот (я думаю) правильный вариант:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    string s = "Haven't got an idea why."; 
    for (auto beg = s.begin(); beg != s.end();) 
    { 
     cout << *beg << '\n'; 
     if (*beg == 'a') 
     {//whithout if construct it works perfectly 
      beg = s.erase(beg); 
     } 
     else 
     { 
      ++beg; 
     } 
    } 
} 

EDIT: Я предлагаю принимать ответ FredOverflow в. Это проще и быстрее, чем указано выше.

+0

«Не кэшируйте значение s.end(), оно изменяется при удалении элементов». Одна из моих основных проблем с 'auto'. Посмотрите на 'auto end = s.end()' и скажите мне, что это не вводит в заблуждение для новичков языка! [общее замечание; не ссылаясь конкретно на этот OP] –

+0

@ Томалак: Я согласен с тем, что непривилегированное значение 'auto' является неудачным, но вы предполагаете, что выбор использовать' auto' был неправильным? –

+0

@ Марсело: Совсем нет! –

4

Предыдущее значение s.end(), сохраненное в end, недействительно после s.erase(). Следовательно, не используйте его.

+0

Я уверен, что вы правы, но это не имеет большого смысла, не так ли? Почему, если конечный итератор недействителен? –

+1

@ A-ha: По определению он признан недействительным. Стандарт так говорит. –

+1

@ A-ha Что говорит стандарт (как правильно указал Марсело), ​​есть веская причина. Итератор смотрел на конечную позицию вашей строки, и вы удалили один ее символ. Логично, что его предыдущее значение больше недействительно. Если вы хотите проверить новый конец, вы должны снова спросить контейнер (s.end()), а не использовать ранее кэшированное значение. –

1

Обратите внимание на семантику basic_string и ее итераторов.

От www.ski.com/tech/stl

Следует также отметить, что, согласно стандарту C++, basic_string имеет очень необычный итератора недействительности семантику. Итераторы могут быть аннулированы путем замены, резервирования, вставки и стирания (и с помощью функций, эквивалентных вставке и/или стиранию, таких как очистка, изменение размера, добавление и замена). Кроме того, однако, первый вызов любой не-константной функции-члена, включая неконстантную версию begin() или operator [], может привести к недействительности итераторов. (Цель этих правил итератора недостоверности, чтобы дать реализаторы большую свободу в методах осуществления.)

Кроме того, что произойдет, если

beg = s.erase(beg); 

Возвращает итератор эквивалент до конца()

6

Стирание элементов один за другим из векторов или строк имеет квадратичную сложность. Существуют лучшие решения с линейной сложностью:

#include <string> 
#include <algorithm> 

int main() 
{ 
    std::string s = "Haven't got an idea why."; 
    s.erase(std::remove(s.begin(), s.end(), 'a'), s.end()); 
    std::cout << s << std::endl; 
} 
1

При вызове операции стирания сохраненный указатель конечного итератора становится недействительным. Итак, используйте функцию s.end() в режиме цикла

0

Вам необходимо выполнить итерацию с .end() - 1 до .begin(). В то же время нецелесообразно использовать операторы сравнения, отличные от == и! =.

Вот мой код:

vector<long long> myVector (my, my+myCount); 
    //sort and iterate through top correlation data counts 
    sort (myVector.begin(), myVector.end()); 
    cout << endl; 
    int TopCorrelationDataCount = 0; 
    bool myVectorIterator_lastItem = false; 
    vector<long long>::iterator myVectorIterator=myVector.end()-1; 
    while (true) {      
     long long storedData = *myVectorIterator; 
     cout << TopCorrelationDataCount << " " << storedData << endl;      

     //prepare for next item 
     TopCorrelationDataCount++; 
     //if (TopCorrelationDataCount >= this->TopCorrelationDataSize) break; 
     if (myVectorIterator_lastItem) break; 
     myVectorIterator--; 
     if (myVectorIterator==myVector.begin()) 
     { 
      myVectorIterator_lastItem = true; 
     } 
    } 

. Примечание: Это не может быть сделано с помощью обычной для, потому что вы должны выяснить, если == начать(). Если да, это будет ваша последняя итерация. Вы не можете проверить, является ли ==. Begin() - 1, так как это приведет к ошибке времени выполнения.

Если вы хотите использовать только элементы X в векторе, используйте TopCorrelationDataCount.