为什么QList没有resize()方法?
Why doesn't QList have a resize() method?
我刚刚注意到QList
没有resize
方法,而QVector
,例如,有一个。这是为什么呢?有没有等效的功能?
嗯,这是更通用的答案,但我希望你能看到,通过QList
和QVector
为什么不需要手动扩展容器。
QList 使用内部缓冲区来保存指向元素的指针(或者,如果元素小于指针大小,或者元素是共享类之一 - 元素本身),并且实际数据将保留在堆上。
在此期间,删除数据不会减少内部缓冲区(空白空间将通过向左或向右移动元素来填充,在开头和结尾留下空间以供以后插入)。
附加项目,如QVector
将在数组的末尾创建额外的新空间,并且由于与QVector
不同,真实数据不存储在内部缓冲区中,因此无论项目的大小如何,您都可以在单个指令中创建大量空间(与QVector
不同) - 因为您只是将指针添加到索引缓冲区中。
例如,如果您使用的是 32 位系统(每个指针 4 个字节),并且在QList
中存储了 50 个项目,并且每个项目的大小为 1MB,则缓冲区的大小需要调整为 50MB QVector
,并且QList
的内部缓冲区只需要分配 200B 的内存。这是您需要在 QVector
中调用resize()
的地方,但QList
没有必要,因为分配小块内存没有问题,因为分配 50MB 内存。
有代价的,这意味着您有时想要QVector
而不是QList
:对于存储在QList
中的单个项目,您需要在堆上增加一个分配 - 以保留项目的真实数据(内部缓冲区中的指针指向的数据)。如果要添加比指针大的 10000 个项目(因为,如果指针可以容纳,它将直接存储在内部缓冲区中),则需要 10000 次系统调用才能为堆上的 10000 个项目分配数据。但是,如果您使用的是QVector
,并且您调用resize
,则可以在单个分配调用中容纳所有项目 - 因此,如果您需要大量插入或追加,请不要使用QList
,更喜欢QVector
。当然,如果您使用 QList
来存储共享类,则不需要额外的分配,这再次使QList
更合适。
因此,在大多数情况下,首选QList
,因为它是:
- 使用索引访问单个元素,访问项目的速度将比
QLinkedList
- 插入列表中间只需要移动指针来创建空间,并且比移动实际
QVector
数据更快。 - 无需手动保留或调整空间大小,因为空白空间将被移动到缓冲区的末尾以供以后使用,并且在数组中分配空间非常快,因为元素非常小,它可以分配大量空间而不会杀死您的内存空间。
请勿在以下情况下使用它,最好QVector
:
- 如果需要确保数据存储在顺序内存位置
- 如果您很少在随机位置插入数据,但要附加大量数据在结尾或开头,这可能会导致大量不必要的系统调用,并且您仍然需要快速索引。
- 如果您正在寻找不会随着时间的推移而增长的简单数组的(共享)替代品。
最后,请注意:QList
(和QVector
)具有reserve(int alloc)
函数,如果alloc
大于内部缓冲区的当前大小,将导致QList
的内部缓冲区增长。但是,这不会影响QList
的外部大小(size()
将始终返回列表中包含的确切元素数)。
原因是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());
}
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声明的构造函数"),否则它将失败。
只需使用类似的东西
QList<Smth> myList;
// ... some operations on the list here
myList << QVector<Smth>(desiredNewSize - myList.size()).toList();
从本质上讲,到处都有这些to
/from
Vector
/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()
可能变得多么复杂的一些背景知识,请参阅:
- bugreports.qt.io/browse/QTBUG-42732 ,以及
- codereview.qt-project.org/#/c/100738/1//ALL
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- vector.resize()中的分配错误
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- C++从另一个类访问公共静态向量的正确方法是什么
- vector的resize方法背后的设计原理是什么?
- vector.resize()方法在调整大小时调用默认的元素构造函数
- 向量resize(), push.back(), reserve()方法
- 为什么QList没有resize()方法?