13

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

4

4 回答 4

10

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

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

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

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

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

但是,有一个代价,这意味着您有时想要优先QVector选择QListQList内部缓冲区指向)。如果要添加比指针大的 10000 个项目(因为如果它可以放入指针,它将直接存储在内部缓冲区中),您将需要 10000 次系统调用来为堆上的 10000 个项目分配数据。但是,如果您正在使用QVector,并且您调用,则可以在单个 alloc 调用中容纳所有项目 - 因此,如果您需要大量插入或附加,resize请不要使用,更喜欢这样做。当然,如果你正在使用QListQVectorQList存储共享类,不需要额外的分配,这再次使得QList更合适。

因此,QList在大多数情况下更喜欢它:

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

不要在以下场景中使用它,并且更喜欢QVector

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

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

于 2013-02-04T12:59:23.100 回答
9

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

但是,如果您真的需要调整 a 的大小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());
}
于 2013-03-13T17:30:36.547 回答
0

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" 声明的构造函数),否则它将失败。

于 2017-05-17T17:09:05.153 回答
-3

只需使用类似的东西

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

本质上,到处都有这些 ///方法tofrom Vector这使得在必要时以某种手动但微不足道且有效(我相信)的方式调整 Qt 容器的大小变得微不足道。ListSet()

另一种(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()可能变得多么复杂的一些背景,请参阅:

于 2016-01-27T21:40:42.593 回答