QTreeWidgetItem析构函数和QTreeWidgetItemIterator有效性

问题描述 投票:0回答:1

为什么删除QTreeWidgetItem *会自动推进与同一QTreeWidget关联的迭代器?在我看的任何地方,这种行为似乎都不是documented。我希望在删除QTreeWidget *之后,迭代器不受影响(除了现在指向无效值)。相反,似乎QTreeWidget析构函数会自动推进QTreeWidget的所有迭代器。

简要介绍一下qt源代码,我认为这可以让所有迭代器在创建模型时将自己注册到模型中。然后在QTreeItem::~QTreeItem中,对QTreeModel::beginRemoveItems()的调用调用了QTreeWidgetItemIteratorPrivate::ensureValidIterator(),它推进了所有的迭代器。

除了直接检查所有QT源之外,你怎么知道这发生了?所有QT迭代器都是这样做的吗?我对STL迭代器比使用QT有更多的经验,也许这就是为什么我发现这是如此意外。

下面的代码演示了这种行为:

    #include <QTreeWidget>
    #include <QDebug>
    #include <QApplication>

    int main(int argc, char **argv)
    {
        QApplication a( argc, argv );
        QTreeWidget * tw_1 = new QTreeWidget();
        QTreeWidget * tw_2 = new QTreeWidget();
        for(int i = 1; i <=5; i++)
        {
                tw_1->addTopLevelItem(new QTreeWidgetItem(QStringList(QString("Item %1").arg(i))));
                tw_2->addTopLevelItem(new QTreeWidgetItem(QStringList(QString("Item %1").arg(i))));
        }

        {
        qDebug() << "Elements in Tree Widget 1";
        QTreeWidgetItemIterator it(tw_1);
        while(*it)
        {
                qDebug() << "\t" << (*it)->text(0);
                ++it;
        }

        //Delete the items while iterating over the tree
        QTreeWidgetItemIterator ii(tw_1);
        while(*ii)
        {
                delete *ii;
        }

        qDebug() << "Elements remaining in Tree Widget 1";
        QTreeWidgetItemIterator itt(tw_1);
        while(*itt)
        {
                qDebug() << (*itt)->text(0);
                ++itt;
        }
        }

        {
        qDebug() << "Elements in Tree Widget 2";
        QTreeWidgetItemIterator it(tw_2);
        while(*it)
        {
                qDebug() << "\t" << (*it)->text(0);
                ++it;
        }

        //Delete the items while iterating over the tree
        QTreeWidgetItemIterator ii(tw_2);
        while(*ii)
        {
                delete *ii;
                ++ii;
        }
           qDebug() << "Elements remaining in Tree Widget 2";
        QTreeWidgetItemIterator itt(tw_2);
        while(*itt)
        {                                                                                                                     
                qDebug() << (*itt)->text(0);                                                                                  
                ++itt;                                                                                                        
        }                                                                                                                     
        }                                                                                                                     
}  

这会产生输出:

Elements in Tree Widget 1 
     "Item 1" 
     "Item 2" 
     "Item 3" 
     "Item 4" 
     "Item 5" 
Elements remaining in Tree Widget 1 
Elements in Tree Widget 2 
     "Item 1" 
     "Item 2" 
     "Item 3" 
     "Item 4" 
     "Item 5" 
Elements remaining in Tree Widget 2 
     "Item 2" 
     "Item 4"  
c++ qt qtreewidget qtreewidgetitem
1个回答
0
投票

迭代器被定义为QList中的QTreeModel

QList是基于指数的。如果删除了具有当前索引的项目,则下一个项目将成为当前索引。

请参阅以下链接。您将看到迭代器的定义,如下所示。

https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidget_p.h.html#QTreeModel

你会看到这样的定义

QList<QTreeWidgetItemIterator*> iterators;

现在,当你在迭代器上调用delete时,会调用QTreeWidgetItemIterator的析构函数。

https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidgetitemiterator.cpp.html#_ZN23QTreeWidgetItemIteratorD1Ev

QTreeWidgetItemIterator::~QTreeWidgetItemIterator() 
{ 
    d_func()->m_model->iterators.removeAll(this); 
} 

通过注意上面的代码,使用QList函数removeAll删除迭代器,并从列表中完全删除迭代器。

QList是基于指数的。因此,如果删除具有索引3的迭代器,则下一个迭代器索引将变为3。

现在当你做++时,

调用下面的函数,它会带来列表中的下一个项目。所以你将迭代器移动到下一个项目(索引4)。

https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidgetitemiterator.cpp.html#_ZN23QTreeWidgetItemIteratorD1Ev

QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator++() 
{ 

    if (current) 
        do { 
            current = d_func()->next(current); 
        } while (current && !matchesFlags(current)); 
    return *this; 
} 

该模型的QTreeWidgetItemIterator维持为QList。因此,当您删除迭代器时,下一个迭代器将成为当前索引。所以你确实注意到了这种行为。

© www.soinside.com 2019 - 2024. All rights reserved.