c++ - 重新访问QList与QVector

标签 c++ qt qt5 qlist qvector

我的问题基本上是何时选择QVector和何时选择QList作为您的Qt容器。我已经知道的:

  • Qt文档:QList class

  • For most purposes, QList is the right class to use. Its index-based API is more convenient than QLinkedList's iterator-based API, and it is usually faster than QVector because of the way it stores its items in memory. It also expands to less code in your executable.


  • 这是非常受欢迎的问答:QVector vs QList。它还支持QList。
  • 但是:在最近的2015年Qt世界峰会上,KDAB提出“为什么QList有害”,基本上是在这里:

  • QList considered harmful

    Don't use QList, use Q_DECLARE_TYPEINFO

    据我了解,这种想法是,在堆中分配新元素时,几乎所有类型的QList效率都不高。每次添加新元素时,它都会调用new(每个元素一次),这与QVector相比效率低下。

    这就是为什么我现在想要理解的原因:我们应该选择QVector作为默认容器吗?

    最佳答案

    Qt宣传QList为“万事通”,但那句话的另一半是“无主”。我想说QList是一个不错的选择,如果您打算追加到列表的两端,并且它们不大于指针,因为QList前后都保留了空间。就是这样,我的意思是就使用QList而言,有充分的理由。
    QList会自动将“大”对象存储为指针并在堆上分配对象,如果您是婴儿,这不知道如何声明QVector<T*>并使用动态分配,这可能是一件好事。这不一定是一件好事,在某些情况下,它只会使内存使用量过大,并增加额外的间接访问。 IMO总是明确指出所需的内容(无论是指针还是实例)都是一个好主意。即使您确实希望进行堆分配,也总是最好自己分配它,并简单地将指针添加到列表中,而不是一次构造该对象,然后在堆上进行复制构造。

    Qt会在很多地方为您返回QList,例如在获得QObject的 child 或您搜索 child 时。在这种情况下,使用在第一个元素之前分配空间的容器是没有意义的,因为它是已经存在的对象的列表,而不是您可能会喜欢的对象。我也不太喜欢缺少resize()方法。

    想象一下这样的情况:在64位系统上,您有一个9字节长且字节对齐的对象。对于QList来说“太多了”,因此它将使用8字节指针+ CPU开销进行缓慢的堆分配+内存开销进行堆分配。它将使用两倍的内存,并且具有额外的间接访问权限,因此几乎不会像所宣传的那样提供性能优势。

    关于QVector为什么不能突然成为“默认”容器的原因-您不要在赛程中改变马匹-这是一件很古老的事情,Qt是如此旧的框架,即使很多东西已被弃用,也要进行更改并非总是可能使用广泛使用的默认值,并非不破坏大量代码或产生不良行为。无论好坏,QList在整个Qt 5中都可能一直是默认设置,并且在下一个主要发行版中也可能如此。在智能指针已成为必需品多年之后,并且所有人都在提示普通指针有多糟糕以及如何永远不使用它们之后,Qt将继续使用“哑”指针的原因相同。

    话虽这么说,没有人强制在设计中使用QList。没有理由不将QVector设为默认容器。我本人在任何地方都不使用QList,在返回QList的Qt函数中,我只是用作将内容移动到QVector的临时方法。

    此外,这只是我个人的观点,但是我确实在Qt中发现了许多不必要的设计决策,例如性能或内存使用效率或明智的易用性,并且总体上有很多框架和喜欢推广自己的做事方式的语言,不是因为这是最好的做事方式,而是因为这是他们做事的方式。

    最后但并非最不重要的:

    For most purposes, QList is the right class to use.



    真正归结为您如何理解这一点。国际海事组织在这种情况下,“正确”并不代表“最佳”或“最佳”,而是代表“足够好”,就像“即使不是最佳也可以做到”。特别是如果您对不同的容器类及其工作方式一无所知。

    For most purposes, QList will do.



    总结一下:
    QList专业人士

    您打算在
  • 前面添加不大于指针大小的对象,因为它在前面的
  • 中保留了一些空间
    您打算将
  • 插入列表对象的中间(基本上)大于指针(并且我在这里很慷慨,因为您可以轻松地将QVector与显式指针一起使用,以实现相同且更便宜的方式-无需额外的复制),因为调整列表大小,将不移动任何对象,仅移动指针
  • QList缺点
  • 没有resize()方法,reserve()是一个细微的陷阱,因为它不会增加有效列表的大小,即使索引访问有效,它也属于UB类别,您也将无法迭代该列表
  • 当对象大于指针时,
  • 会进行额外的复制和堆分配,如果对象身份很重要,那么这也可能是一个问题
  • 使用额外的间接访问来访问大于指针
  • 的对象
    由于最后两个,
  • 具有CPU时间和内存使用开销,并且对缓存友好的
  • 也较少
  • 用作“搜索”返回值时会带来额外的开销,因为您不太可能在该
  • 之前或什至追加它
  • 仅在必须进行索引访问的情况下才有意义,为了获得最佳的前缀和插入性能,链接列表可能是一个更好的选择。

  • CON的优势略大于PRO,这意味着虽然可以“随意”使用QList,但您绝对不希望在CPU时间和/或内存使用成为关键因素的情况下使用它。总而言之,当您不想为用例考虑最佳存储容器时,QList最适合懒惰和粗心的使用,通常是QVector<T>QVector<T*>QLinkedList(但我排除了“STL“容器,因为我们在这里谈论Qt,所以Qt容器具有可移植性,有时更快,并且最肯定更易于使用和清洁,而std容器则是不必要的冗长。

    关于c++ - 重新访问QList与QVector,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33609406/

    相关文章:

    c++ - 从 QCompleter 中选择项目后无法清除 QLineEdit

    c++ - 从 C++ 实例化 QML

    c++ - 如何用某种颜色填充 QGraphicsRectItem?

    c++ - IF 的原子性和下面的语句

    c++ - 类成员的typedef

    c++ - 如何获取所有系统进程的GuiResources?

    c++ - 如何存储Qt gui布局并恢复它

    c++ - DOM 解析器中的 SetContent 错误

    qt - 属性定义运算符 "type"字段中的点符号

    C++ - 来自 std::string 的意外输出