Доступ к элементу списка, указанному итератором - PullRequest
4 голосов
/ 28 февраля 2011

Естественным ответом будет разыменование итератора и получение значения. Тем не менее, я застрял в использовании VC ++ 2010, который не позволяет разыменовывать итератор списка (или нет?) Я запутался, потому что в какой-то момент мне нужно разыменовать два итератора списка и сравнить их значения, используя: (* это) == (* это 2) Программа вылетает с ошибкой, только из-за этой строки. Я также разыменовываю итератор в выражении: printf ("% d \ n", (* it)); Это работает прекрасно, хотя. Итак, есть ли способ получить доступ к элементу без разыменования или использования cliext :: list.

for (it=sList.begin(); it != sList.end(); it++)
{
    for (it2=it; it2 != sList.end(); it2++)
    {
        it2++;
        if ((*it) == (*it2))
        {
           sList.erase(it, it2);
        }
        it2--;
    }
}

Я получаю ошибку:

Ошибка отладки

Выражение: итератор списка не может быть разыменован

Удивительно, но тот же код выполняется без проблем при компиляции на DevC ++ (MinGW)

Ответы [ 5 ]

4 голосов
/ 28 февраля 2011

На самом деле вы можете разыменовать list итераторы.Если бы вы не смогли, ваш код сравнения даже не скомпилировался бы.Скорее всего, вы случайно разыменовываете итератор end, а не действительный, вызывая сбой.Без большего количества кода трудно делать дальнейшие наблюдения.

РЕДАКТИРОВАТЬ: Я не могу понять, что именно вы пытаетесь сделать.Код, который вы написали, стирает все элементы между двумя равными элементами.Я предполагаю, что вы на самом деле пытаетесь удалить все повторяющиеся элементы, и сортировка списка в первую очередь по производительности не является проблемой / возможностью.

РЕДАКТИРОВАТЬ2: Я видел в комментарии ниже, что вы действительно хотитеудалить диапазон.Обновил мой код.

Затем попробуйте что-то вроде этого:

for (it=sList.begin(); it != sList.end(); ++it)
{
    it2 = it;
    ++it2;
    while(it2 != sList.end())
    {
        if ((*it) == (*it2))
        {
           it = it2 = sList.erase(it, it2);  // Reset it as well since it will be blown away. It'll still point to the same value it did before though.
        }
        else
            ++it2;
    }
}
1 голос
/ 01 марта 2011

Попробуйте вместо этого:

for (it=sList.begin(); it != sList.end(); it++)
{
    for (it2=sList.end()-1; it2 != it+1; it2--)
    {
        if ((*it) == (*it2))
        {
            it = sList.erase(it, it2)-1;
            break;
        }
    }
}

Эта новая версия позволяет избежать двух ошибок в исходной версии кода. Во-первых, код теперь правильно обрабатывает краевые условия внутреннего цикла for. В исходном коде цикл for позволил it2 подняться до sList.end()-1, но затем следующая строка увеличила его до sList.end() на последней итерации. Следующая строка затем разыменовывает этот (недопустимый) итератор, который находится на единицу после последнего значения списка (потому что это то, что возвращает end, это не итератор до последнего значения списка).

Во-вторых, вызов erase делает недействительными любые итераторы, указывающие на любое из удаленных значений (которые в этом случае будут включать любые итераторы от it до it2-1). Начав с конца списка и продвигаясь вперед, нам больше не нужно продолжать итерации, когда мы находим значение, и можем break из внутреннего цикла, как только мы его найдем. erase возвращает итератор для следующего элемента в списке после удаленных элементов (который будет следующим элементом, который мы хотим использовать для it). Но поскольку цикл for увеличивает it, мы вычитаем 1 из того, что возвращает erase, так что it указывает на правый элемент после его увеличения в начале следующей итерации цикла. (Обратите внимание, что в случае, когда it указывает на первый элемент, мы фактически временно устанавливаем его так, чтобы он указывал на элемент перед началом списка; однако это только временно, и мы не разыменовываем итератор, когда он указывает вне список).

Обратите внимание, что это сохраняет исходное поведение кода для случая 0 2 3 4 5 1 6 7 8 0 9 10 11 1. Вы явно не указали, в каком порядке должны выполняться удаления (если элементы между 0 будут удалены первыми, или элементы между 1, или нам нужно добавить дополнительную логику, чтобы фактически стереть весь диапазон за исключением первых 0 и 1?), но этот код ведет себя как оригинал и стирает числа между 0 и игнорирует тот факт, что впоследствии 9 10 11 был оригинальным между совпадениями 1 s.

1 голос
/ 28 февраля 2011

Это, безусловно, ваш код.Насколько я вижу, у него есть две проблемы.Оформить заказ комментариев.

for (it2=it; it2 != sList.end(); it2++)
    {
        it2++;
            // there is no guarantee that it2 will now be valid
            // it should be validated again
        if ((*it) == (*it2))
        {
            // you should not modify the list here.
            // this will invalidate your iterators by default.
           sList.erase(it, it2);
        }
        it2--;
    }
1 голос
/ 28 февраля 2011

"выберите" не сломан.

Редко можно найти ошибку в ОС или компилятор, или даже сторонний продукт или библиотека. Ошибка самая скорее всего в приложении. - от Прагматичный программист

Скорее всего, это связано с вашей проблемой, а не с MS. Убедитесь, что ваши итераторы не аннулированы , пока вы их используете. Вы можете случайно стереть элемент, который делает недействительным итератор. Проверьте эту ветку: Каков срок службы и срок действия итераторов C ++? и удачи! :)

UPDATE:

Как я упоминал ранее, вы делаете недействительными ваши итераторы, стирая их в середине цикла. Смотрите мой код ниже, чтобы сделать это правильно.

std::list<int>::iterator EraseElements(std::list<int>& sList, std::list<int>::iterator start)
{
    for (std::list<int>::iterator itor1 = start; itor1 != sList.end(); ++itor1)
    {
        std::list<int>::iterator itor2(itor1);
        ++itor2;

        for ( ; itor2 != sList.end(); ++itor2)
        {
            if ((*itor1) == (*itor2))
            {
                return sList.erase(itor1, itor2);
            }
        }
    }

    return sList.end();
}


void main()
{
    // Test
    list<int> sList;
    sList.push_back(1);

    // elements will be erased
    sList.push_back(2);
    sList.push_back(3);
    //
    sList.push_back(2);

    sList.push_back(4);
    sList.push_back(5);

    // elements will be erased
    sList.push_back(6);
    sList.push_back(7);
    //
    sList.push_back(6);


    list<int>::iterator next = sList.begin();
    while (next != sList.end())
    {
        next = EraseElements(sList, next);
    }

    // It will print 1 2 4 5 6
    for (std::list<int>::const_iterator itor = sList.begin(); itor != sList.end(); ++itor)
    {
        cout << *itor << endl;
    }
}
0 голосов
/ 28 февраля 2011

Действительно неясно, что пытается сделать этот фрагмент кода или любой другой код, из которого вы получаете ошибку.

Похоже, что вы хотите сделать, чтобы для каждого элемента были удалены все элементы между ним и следующим соответствующим элементом, или, возможно, это последний соответствующий элемент.

Ваша внутренняя итерация состоит из двойного шага от приращения цикла и последующего приращения внутри цикла.

Вы не проверяете, попали ли вы в конец списка после выполнения внутренней итерации, что может привести к сбою при сравнении

после стирания вы уменьшаете значение it2, которое затем ставит его перед тем, что было1 (и теперь удаляется).

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