【问题标题】:Why doesn't QList have a resize() method?为什么 QList 没有 resize() 方法?
【发布时间】:2013-01-18 22:35:29
【问题描述】:

我刚刚注意到QList 没有resize 方法,而例如QVector 有一个。为什么是这样?有没有等价的功能?

【问题讨论】:

标签: c++ qt qlist


【解决方案1】:

嗯,这是更通用的答案,但我希望您通过比较 QListQVector 来了解为什么不需要手动扩展容器。

QList 正在使用内部缓冲区来保存指向元素的指针(或者,如果元素小于指针大小,或者元素是 shared classes 之一 - 元素本身),真实数据将保存在堆上.

在此期间,删除数据不会减少内部缓冲区(空白空间将通过向左或向右移动元素来填充,在开头和结尾留出空间供以后插入)。

追加项目,如QVector 将在数组末尾创建额外的新空间,因为与QVector 不同,实际数据不存储在内部缓冲区中,您可以创建很多单个指令中的空间,无论项目的大小是多少(与QVector 不同) - 因为您只是将指针添加到索引缓冲区中。

例如,如果您使用 32 位系统(每个指针 4 个字节)并且您在 QList 中存储 50 个项目,并且每个项目是 1MB 大,QVector 缓冲区将需要调整为 50MB,并且QList 的内部缓冲区只需要分配 200B 的内存。这是您需要在QVector 中调用resize() 的地方,但在QList 中则不需要,因为分配小块内存没有问题,因为分配50MB 内存。

然而,有一个代价,这意味着您有时想要使用QVector 而不是QList:对于存储在QList 中的单个项目,您需要在堆上增加一个分配器——以保持项目的真实数据(内部缓冲区中的指针指向的数据)。如果要添加比指针大的 10000 项(因为如果可以放入指针,它将直接存储在内部缓冲区中),您将需要 10000 次系统调用来为堆上的 10000 项分配数据。但是,如果您使用QVector,并且调用resize,则可以在单个alloc 调用中容纳所有项目-因此,如果您需要大量插入或附加,请不要使用QList,更喜欢QVector 为此。当然,如果你使用QList来存储共享类,就不需要额外分配,这又让QList更合适了。

所以,在大多数情况下,更喜欢QList

  1. 使用索引访问单个元素,访问项目将比QLinkedList 更快
  2. 插入到列表中间只需要移动指针来创建空间,它比移动实际的QVector 数据要快。
  3. 无需手动预留或调整空间大小,空的空间会被移到缓冲区的末尾以备后用,而且在数组中分配空间非常快,因为元素非常小,而且可以在不占用内存空间的情况下分配大量空间。

以下场景不要使用,首选QVector

  1. 如果您需要确保数据存储在顺序内存位置中
  2. 如果您很少在随机位置插入数据,但会追加 很多 数据 在结尾或开头,这可能会导致很多不必要的系统调用,并且您仍然需要快速索引。
  3. 如果您正在寻找(共享)简单数组的替代品,这些数组不会随着时间的推移而增长。

最后,请注意:QList(和QVector)具有reserve(int alloc) 函数,如果alloc 大于内部缓冲区的当前大小,这将导致QList 的内部缓冲区增长。但是,这不会影响QList外部大小size() 将始终返回列表中包含的元素的确切数量)。

【讨论】:

  • 虽然您的解释非常合理,但调用resize 可能还是不错的,不是作为优化,而是作为缩小或增长列表的一种手段(必要时填写默认值) , 因为您的算法/设计需要一个恰好包含 n 个元素的列表
  • 顺便说一句,“但是你在末尾或开头添加了很多数据” - 在开头添加很多数据仍然不是最好的主意,因为这也需要移动其他元素(好吧,如果你的意思是一次预先放置多个元素的大块,它可能会再次摊销自己)。否则合理的推理。 +1
  • @ChristianRau 感谢您的 cmets,我将编辑我的答案以添加您在此处指出的事实。由于QList 中没有resize,我认为reserve 在这种情况下是一种方法,对吧?
  • 不,不是。 reserve 反过来 一个单纯的优化,它并没有真正改变外界对列表的看法(元素的数量),而 resize 用于如果你真的想要列表更改其外部大小。如果QList 没有resize (出于您描述的概念原因),那么如果您需要它,您就很不幸。看到你提议用reserve 代替resize,我会把我的赞成票放在冰上,直到它解决为止。
  • 我在这里找到了一个关于这个问题的帖子 - lists.trolltech.com/qt4-preview-feedback/2005-06/…。似乎其中一个原因是他们不想强制执行默认构造函数,但我不确定这是否是一个正当理由。
【解决方案2】:

我认为原因是因为QList 不需要元素类型具有默认构造函数。 因此,QList 不会创建一个对象,它只会复制它们。

但如果你真的需要调整QList 的大小(无论出于何种原因),这里有一个函数可以做到。请注意,它只是一个便利函数,并没有考虑到性能

template<class T>
void resizeList(QList<T> & list, int newSize) {
    int diff = newSize - list.size();
    T t;
    if (diff > 0) {
        list.reserve(newSize);
        while (diff--) list.append(t);
    } else if (diff < 0) list.erase(list.end() + diff, list.end());
}

【讨论】:

  • 请注意,“不要求元素类型具有默认构造函数”是一个实现细节。 QList 被记录为需要一个默认可构造的类型。在这一点上,“细节”不太可能改变,仍然......
  • 应该是list.reserve(newSize);
【解决方案3】:

wasle 答案很好,但它会多次添加同一个对象。这是一个实用函数,它将为智能指针列表添加不同的对象。

template<class T>
void resizeSmartList(QList<QSharedPointer<T> > & list, int newSize) {
    int diff = newSize - list.size();

    if (diff > 0) {
        list.reserve(diff);
        while (diff>0){
            QSharedPointer<T> t = QSharedPointer<T>(new T);
            list.append(t);
            diff--;
        }
    }else if (diff < 0) list.erase(list.end() + diff, list.end());
}

对于没有智能指针的使用,以下将向您的列表中添加不同的对象。

template<class T>
void resizeList(QList<T> & list, int newSize) {
    int diff = newSize - list.size();

    if (diff > 0) {
        list.reserve(diff);
        while (diff>0){
            T t = new T;
            list.append(t);
            diff--;
        }
    }else if (diff < 0) list.erase(list.end() + diff, list.end());
}

还请记住,您的对象必须具有默认构造函数(在标头中使用 arg="someValue" 声明的构造函数),否则它将失败。

【讨论】:

    【解决方案4】:

    只需使用类似的东西

    QList<Smth> myList;
    // ... some operations on the list here
    myList << QVector<Smth>(desiredNewSize - myList.size()).toList();
    

    基本上,到处都有to/fromVector/List/Set() 方法,这使得在必要时通过手动调整 Qt 容器大小变得微不足道,但微不足道且有效(我相信)方式。

    另一种(1 或 2 线)解决方案是:

    myList.reserve(newListSize); // note, how we have to reserve manually
    std::fill_n(std::back_inserter(myList), desiredNewSize - myList.size(), Smth());
    

    -- 那是为面向 STL 的人准备的 :)

    有关有效的QList::resize() 可能变得多么复杂的一些背景知识,请参阅:

    【讨论】:

    • 对于And is there an equivalent function?,这非常令人惊讶,没有人(4K 观众)查看文档以获取可能相关的签名 :)
    • @Downvoting 胆小鬼,当你有足够的精神时,让我们说话吧:)
    • 这完全没有意义。问题是QList 是否缺少resize 方法。答案是:因为 Qt 容器有很多问题,而这只是另一个问题。
    • 但问题不在于“创建给定大小的 QList”,即使是这样,从/到 QVector 的转换也是一派胡言。这是 C++,不是 Python 或 Perl!我们确实关心效率!
    • 我想知道 是编写该代码的人,嗯...(提示:我)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 2011-07-16
    • 2015-05-01
    • 2014-03-07
    • 2011-06-12
    • 2019-04-08
    • 2015-11-26
    相关资源
    最近更新 更多