为什么QList没有resize()方法?

时间:2013-02-04 08:47:37

标签: c++ qt qlist

我刚注意到QList没有resize方法,而QVector例如有{{1}}方法。为什么是这样?是否有相同的功能?

4 个答案:

答案 0 :(得分:10)

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

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

在此期间,删除数据不会减少内部缓冲区(空白区域将通过左移或右移元素填充,在开始和结束时留出空间以便以后插入)。

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

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

但是,这需要付出代价,这意味着您有时希望QVector代替QList:对于存储在QList中的单项,您需要一个额外的分配在堆上 - 保留项的实际数据(内部缓冲区中的指针指向的数据)。如果要添加大于指针的10000个项目(因为,如果它可以适合指针,它将直接存储在内部缓冲区中),则需要10000个系统调用来为堆上的10000个项目分配数据。但是,如果您使用的是QVector,并且调用resize,则可以在单个alloc调用中调整所有项目 - 因此,如果您需要很多内容,请不要使用QList插入或追加,更喜欢QVector。当然,如果您使用QList来存储共享类,则无需额外分配,这使QList更适合。

因此,对大多数情况更喜欢QList

  1. 使用索引访问各个元素,访问项目的速度将快QLinkedList
  2. 插入列表中间只需移动指针即可创建空间,并且比移动实际QVector数据更快。
  3. 不需要手动保留或调整空间大小,因为空的空间将被移动到缓冲区的末尾供以后使用,并且在数组中分配空间非常快,因为元素非常小,它可以分配大量空间而不会占用你的记忆空间。
  4. 请勿在以下情况中使用它,并且更喜欢QVector

    1. 如果您需要确保数据存储在顺序存储位置
    2. 如果您很少在随机位置插入数据,但是您附加很多数据 在结束或开始,这可能会导致许多不必要的系统调用,并且您仍然需要快速索引。
    3. 如果您正在寻找(共享)替换简单数组,这些数组不会随着时间的推移而增长。
    4. 最后,请注意:QList(和QVector)具有reserve(int alloc)函数,如果QListalloc,则会导致QList的内部缓冲区增长大于内部缓冲区的当前大小。但是,这不会影响size()外部大小({{1}}将始终返回列表中包含的确切元素数。)

答案 1 :(得分:7)

我认为原因是因为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(diff);
        while (diff--) list.append(t);
    } else if (diff < 0) list.erase(list.end() + diff, list.end());
}

答案 2 :(得分:0)

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

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

答案 3 :(得分:-3)

只需使用

之类的东西
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()可能有多复杂的背景知识,请参阅: