Странное поведение итератора - PullRequest
0 голосов
/ 16 июня 2010
#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;
}

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

Ответы [ 6 ]

8 голосов
/ 16 июня 2010

Есть несколько проблем с этим кодом.

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

РЕДАКТИРОВАТЬ: Я предлагаю принять ответ FredOverflow. Это проще и быстрее, чем выше.

6 голосов
/ 16 июня 2010

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

#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;
}
4 голосов
/ 16 июня 2010

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

1 голос
/ 16 июня 2010

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

1 голос
/ 16 июня 2010

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

с www.ski.com/tech/stl

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

И что будет, если

 beg = s.erase(beg);

Возвращает итератор, эквивалентный end ()

0 голосов
/ 06 июня 2011

Вы должны выполнить итерацию от .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 ().Если это так, это будет ваша последняя итерация.Вы не можете проверить, если ==. Begin () - 1, так как это приведет к ошибке во время выполнения.

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

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