重新访问QList与QVector [英] QList vs QVector revisited

查看:146
本文介绍了重新访问QList与QVector的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  1. Qt文档: QList类

在大多数情况下,QList是要使用的正确类.它的基于索引的API比QLinkedList的基于迭代器的API更方便,并且由于它在内存中存储项目的方式,它通常比QVector更快.它还会扩展为减少可执行文件中的代码.

  1. 这个非常受欢迎的问答集也写着: QVector vs QList .它还支持QList.

  2. 但是:在最近的2015年Qt世界峰会上,KDAB提出了为什么QList有害"的说法,基本上是在这里:

QList被认为是有害的

不要使用QList,请使用Q_DECLARE_TYPEINFO

据我了解,

几乎所有类型的QList在堆中分配新元素时效率低下.每次添加新元素时,它都会调用new(每个元素一次),这与QVector相比效率低下.

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

解决方案

Qt宣传QList为万事通",但那句话的另一半是万无一失".我想说QList是一个不错的选择,如果您打算追加到列表的两端,并且它们不大于指针,因为QList之前和之后都保留了空间.就是这样,我的意思是就使用QList的充分理由而言.

QList将自动将大"对象存储为指针并在堆上分配对象,如果您是婴儿,不知道如何声明QVector<T*>并使用它,这可能是一件好事动态分配.这不一定是一件好事,在某些情况下,它只会使内存使用量过大,并增加额外的间接访问. IMO总是明确指出所需的内容(无论是指针还是实例)都是一个好主意.即使您确实想要堆分配,也总是最好自己分配它,只需将指针添加到列表中,而不是一次构造该对象,然后在堆上进行复制构造.

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

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

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

话虽如此,没有人强迫在您的设计中使用QList.没有理由不应该将QVector设为您的默认容器.我自己不在任何地方使用QList,在返回QList的Qt函数中,我只是用作将内容移动到QVector的临时对象.

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

最后但并非最不重要:

在大多数情况下,QList是要使用的正确类.

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

在大多数情况下,QList都可以.


总结一下:

QList专业人士

  • 您打算在对象之前添加不超过指针大小的对象,因为它在前面保留了 some 个空间
  • 您打算在列表对象的中间(基本上)插入一个比指针大的对象(并且我在这里很慷慨,因为您可以轻松地将QVector与显式指针一起使用,以实现相同且更便宜的方式-没有多余的副本) ),因为在调整列表大小时,不会移动任何对象,只会移动指针

QList缺点

  • 没有resize()方法,reserve()是一个细微的陷阱,因为它不会增加有效列表的大小,即使索引访问有效,它也属于UB类别,您也将无法重复该列表
  • 在对象大于指针时进行额外的复制和堆分配,如果对象身份很重要,这也可能是一个问题
  • 使用额外的间接访问来访问大于指针的对象
  • 由于后两个因素导致CPU时间和内存使用开销增加,对缓存的友好性也降低了
  • 用作搜索"返回值时会产生额外的开销,因为您不太可能在该值之前或什至附加
  • 仅当必须进行索引访问时才有意义,为了获得最佳的前缀和插入性能,链接列表可能是一个更好的选择.

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

My question is basically when to choose QVector and when to choose QList as your Qt container. What I already know:

  1. Qt docs: 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.

  1. The same is written is this very popular Q&A: QVector vs QList. It also favors QList.

  2. But: on recent Qt World Summit 2015 KDAB presented "Why QList is harmful", this is basically here:

QList considered harmful

Don't use QList, use Q_DECLARE_TYPEINFO

As far as I understand the idea is that QList for almost all types is inefficient when allocating new elements in heap. Each time you are adding new element, it calls new (once per element) and this is inefficient compared to QVector.

This is why now I am trying to understand: is it QVector which we should choose as default container?

解决方案

Qt advertises QList as the "jack of all trades", but the other half of that saying is "master of none". I'd say QList is a good candidate if you plan on appending to both ends of the list, and those are no larger than than a pointer, as QList reserves space before and after. That's about it, I mean as far as good reasons to use QList are concerned.

QList will automatically store "large" objects as pointer and allocate the objects on the heap, which may be considered a good thing if you are a baby, which doesn't know how to declare a QVector<T*> and use dynamic allocation. This is not necessarily a good thing, and in some cases it will only bloat the memory usage and add extra indirection. IMO it is always a good idea to be explicit about what you want, whether it is pointers or instances. Even if you do want heap allocation, it is always better to allocate it yourself and simply add the pointer to the list than construct the object once, then have have copy construct that on the heap.

Qt will return you a QList in a lot of places where it comes with overhead, for example when getting a QObject's children or you search for children. In this case it doesn't make sense to use a container that allocates space before the first element, as it is a list of objects which are already there, not something you are likely to prepend to. I also don't much like the absence of a resize() method.

Imagine a situation where you have an object with size of 9 bytes and byte alignment on a 64 bit system. It is "far too much" for QList so instead it will use 8 byte pointer + CPU overhead for the slow heap allocation + memory overhead for the heap allocation. It will use twice the memory and with an extra indirection for access it will hardly offer performance advantages as advertised.

As of why QVector cannot suddenly become the "default" container - you don't change horses mid-race - it is a legacy thing, Qt being such an old framework, and even though a lot of stuff has been deprecated, making changes to widely used defaults is not always possible, not without breaking a lot of code, or producing undesired behavior. Good or bad, QList will likely continue being the default all the way throughout Qt 5, and likely in the next major release as well. The same reason Qt will continue using "dumb" pointers, for years after smart pointers have become a must and everybody is crying about how bad plain pointers are and how they should not be used ever.

That being said, nobody is forcing you to use QList in your design. There is no reason why QVector should not be your default container. I myself don't use QList anywhere, and in the Qt functions which return a QList I merely use as a temporary to move stuff into a QVector.

Furthermore, and this is only my personal opinion, but I do find a lot of design decisions in Qt that don't necessary make sense, be that performance or memory use efficiency or ease of use wise, and overall there are a a lot of frameworks and languages which like promoting their ways of doing things, not because it is the best way to do it, but because it is their way to do it.

Last but not least:

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

It really boils down to how you understand this. IMO in this context, "the right" does not stand for "the best" or "the optimal", but for "good enough" as in "it will do, even if not the best". Especially if you know nothing about different container classes and how they work.

For most purposes, QList will do.


To sum things up:

QList PROs

  • you intend to prepend objects no larger than the size of a pointer, since it reserves some space in the front
  • you intend to insert in the middle of the list objects (substantially) larger than a pointer (and I am being generous here, since you can easily use QVector with explicit pointers to achieve the same and cheaper - no extra copy), since when resizing the list, no objects will be moved, only pointers

QList CONs

  • doesn't have a resize() method, reserve() is a subtle trap, since it will not increase the valid list size, even if index access works it falls in the UB category, also you will not be able to iterate that list
  • does an extra copy and heap allocating when object is larger than a pointer, which might also be an issue if object identity matters
  • uses extra indirection to access objects larger than a pointer
  • has CPU time and memory usage overheads due to the last two, also less cache friendly
  • comes with additional overhead when used as a "search" return value, since you are not likely to prepend or even append to that
  • only makes sense if index access is a must, for optimal prepend and insert performance a linked list might be a better option.

The CON's marginally outweigh the PROs, meaning that while in "casual" use QList might be acceptable, you definitely don't want to use it in situations where CPU time and/or memory usage are a critical factor. All in all, QList is best suited for lazy and careless use, when you don't want to make the consideration of optimal storage container for the use case, which would typically be a QVector<T>, a QVector<T*> or a QLinkedList (and I exclude "STL" containers, since we are talking Qt here, Qt containers are just as portable, sometimes faster, and most certainly easier and cleaner to use, whereas std containers are needlessly verbose).

这篇关于重新访问QList与QVector的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆