Проблемы с итераторами Qt в QLinkedList - PullRequest
0 голосов
/ 09 июля 2019

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

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

В коде я просто обновляю итератор, который указывает на текущий элемент данных в цепочке, который называется m_currentItem. Затем я использую его для отображения данных текущего элемента, цикла от текущего до конца или до начала. Я требую, чтобы этот итератор был действителен на протяжении всего выполнения и жизненного цикла приложения, даже если элементы добавлены или удалены. Когда пользователь нажимает «следующий» или «предыдущий», итератор просто увеличивается или уменьшается. Когда пользователь удаляет элемент в списке (кроме последнего), итератор затем указывает на следующую запись, и отображается ее содержимое. Когда пользователь добавляет элемент в список, m_currentItem теперь указывает на этот новый элемент.

Здесь все идет не так, как надо: на петлях назад или вперед. Они часто (но не всегда ...) переходят через конечное условие и переходят к неизвестным данным, пока не произойдет серьезный сбой. Иногда он также возвращается к первому элементу в списке (!!!), а затем для вас это бесконечный цикл.

Вот пример, взятый из моего кода, что-то явно не так? Что-нибудь еще, что я должен знать об итераторах Qt?

РЕДАКТИРОВАТЬ:

Только что узнал об обратных итераторах! Кажется, рекомендуется использовать их вместо перегруженного оператора -- на стандартных итераторах! Но как сделать m_currentItems обратным итератором?

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

include <QLinkedList>

struct Member
{
    int memberID;
}

struct Item
{
    /* A map of members, each mapped to an ID */
    QMap<int, Member> m_members;
    QString name;
}

typedef QLinkedList<Item> LinkedItems;

LinkedItems m_items;
LinkedItems::iterator m_currentItem;

...

if(m_currentItem != m_items.end())
{
    for(LinkedItems::iterator iter = m_currentItem+1; iter != m_items.end(); iter++)
    {
        // Do things on *iter
    }
}

Более сложная реализация: в каждом связанном элементе находятся элементы, содержащие данные и идентификатор. Пользователь может распространить идентификатор во всех предыдущих элементах, расположенных перед найденным элементом (который сам должен быть расположен перед текущим элементом), который ему предлагается выбрать.

например. если у нас есть A-B-C-D-E-F и пользователь находится на E, он может распространить идентификатор члена E на C и на предыдущие элементы (также на A и B, но не на D).

Вот код. Большинство или все циклы итератора подвержены неожиданному поведению.

void renameAllPreviousMembers(int memberIDToPropagate)
{
    /* If first item, then there is no previous altogether */
    if(m_currentItem != m_items.begin())
    {
        if(m_currentItem.m_members.contains(memberIDToPropagate) == false)
        {
            QMessageBox::critical(nullptr, tr("Rename members in previous items"),
                                          tr("There is no member with the selected ID in the current item."),
                                          QMessageBox::Ok);
            return;
        }

        bool chose_item;
        LinkedItems::iterator itemIter;
        QStringList previousItems;
        QStringList previousItemsBetterOrder;
        QMap<QString, LinkedItems::iterator> previousItemsMap;

        previousItemsMap.clear();

        /* Find all previous items and store them in a QStringList and a map between those strings and actual items */
        std::cout << "Search previous items : " << std::flush;
        for(itemIter = m_items.begin();
            itemIter != m_currentItem;
            itemIter++)
        {
            /* Possibly add conditions on some items that we do not want to apply values to. */
            std::cout << itemIter->name << ", " << std::flush;
            previousItems << itemIter->name;
            previousItemsMap.insert(itemIter->name, itemIter);
        }
        std::cout << "finished." << std::endl << std::flush;

        QStringList::reverse_iterator riter;

        for(riter = previousItems.rbegin(); riter != previousItems.rend(); riter++)
        {
            previousItemsBetterOrder << *riter;
        }

        QString name_previous_item = QInputDialog::getItem(nullptr,
                                                      QString("Previous item"),
                                                      QString("Previous item to start from : "),
                                                      previousItemsBetterOrder, 0, false, &chose_item);

        if(!chose_item)
        {
            return;
        }

        /* Decode chosen previous item by retrieving it in the map */
        LinkedItems::iterator chosenPrevIter = previousItemsMap.value(name_previous_item);

        bool chose_member;

        /* Find all existing IDs in previous item */

        QStringList previousIDs;

        QMap<int, Member>::const_iterator memberIter;

        /* Retrieve all members from the chosen previous item */

        std::cout << "Search IDs in chosen previous item : " << std::flush;
        for(memberIter = chosenPrevIter->m_members.begin(); memberIter != chosenPrevIter->m_members.end(); memberIter++)
        {
            previousIDs << QString("%1").arg(QString::number(memberIter->ID));
            std::cout << memberIter->memberID << ", " << std::flush;
        }
        std::cout << "finished." << std::endl << std::flush;

        /* If no member then no lunch */
        if(previousIDs.size() == 0)
        {
            QMessageBox::critical(nullptr, tr("Rename previous member"),
                                          tr("There are no members in the selected previous item."),
                                          QMessageBox::Ok, QMessageBox::Ok);
            return;
            std::cout << "Rename previous members finished with no members in chosen previous item." << std::endl << std::flush;
        }

        QString string_member_id_from_previous = QInputDialog::getItem(nullptr,
                                                      QString("Previous member number"),
                                                      QString("Member ID from previous item to be used : "),
                                                      previousIDs, 0, false, &chose_member);
        if(chose_member)
        {
            int member_id_from_previous = string_member_id_from_previous.toInt();


            /* Update member ID at and before chosen previous item */

            std::cout << "Update member ID before chosen previous item : " << std::flush;
            for(itemIter = chosenPrevIter;
                itemIter != m_items.begin();
                itemIter--)
            {
                std::cout << itemIter->name << std::flush;
                if(itemIter->m_members.contains(member_id_from_previous))
                {
                    /* Take and reinsert to new ID. */
                    std::cout << "+" << std::flush;
                    itemIter->m_members.insert(memberIDToPropagate, itemIter->m_members.take(member_id_from_previous));
                }
                else
                {
                    /* Do nothing. */
                }
                std::cout << ", " << std::flush;
            }
            std::cout << "finished." << std::endl << std::flush;

            }
        }
        else
        {
            /* Do nothing. */
        }
    }
    else
    {
        QMessageBox::critical(nullptr, tr("Apply to previous members"),
                             tr("This is the first item. There are no previous items."),
                             QMessageBox::Ok);

    }
    std::cout << "Apply to previous members finished." << std::endl << std::flush;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...