Я знаю, что меня попросят привести минимальный пример, но я не уверен, что смогу вообще его найти, или он не будет очень минимальным. Но я все еще могу извлечь ценные знания из этого обсуждения.
Итак, у меня есть ряд элементов данных данного класса, организованных в 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;
}