Подтвердить что ты не робот

Почему QList не имеет метод resize()?

Я только заметил, что QList не имеет метода resize, а QVector, например, имеет один. Почему это? И есть ли эквивалентная функция?

4b9b3361

Ответ 1

Я думаю, причина в том, что 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

Ну, это более общий ответ, но, надеюсь, вы увидите, сравнив QList и QVector, почему нет необходимости вручную расширять контейнер.

QList использует внутренний буфер для сохранения указателей на элементы (или, если элемент меньше размера указателя, или элемент является одним из разделяемых классов - элементов), а реальные данные будут храниться в куче.

В течение времени удаление данных не уменьшит внутренний буфер (пустое пространство будет заполнено путем смещения левого или правого элементов, оставив пространство в начале и конце для последующих вставок).

Добавляемые элементы, такие как QVector, создадут дополнительное новое пространство в конце массива, и поскольку в отличие от QVector реальные данные не хранятся во внутреннем буфере, вы можете создать много независимо от того, какой размер элемента (в отличие от QVector) - потому что вы просто добавляете указатели в буфер индексирования.

Например, если вы используете 32-битную систему (4 байта на указатель), и вы сохраняете 50 элементов в QList, и каждый элемент имеет размер 1 МБ, буфер QVector должен быть изменен до 50 МБ, а Внутренний буфер QList должен выделять только 200B памяти. Здесь вам нужно вызвать resize() в QVector, но в QList нет необходимости, так как выделение небольшого фрагмента памяти не является проблематичным, так как выделяет 50 МБ памяти.

Однако для этого есть цена, которая означает, что вы иногда предпочитаете QVector вместо QList: для одного элемента, хранящегося в QList, вам нужно еще одно выделение в куче - сохранить реальные данные элемента (данные, на которые указывает указатель во внутреннем буфере). Если вы хотите добавить 10000 элементов больше, чем указатель (потому что, если он может поместиться в указатель, он будет храниться непосредственно во внутреннем буфере), вам потребуется 10000 системных вызовов для распределения данных для 10000 элементов в куче. Но если вы используете QVector, и вы вызываете resize, вы можете поместить все элементы в один вызов выделения, поэтому не используйте QList, если вам нужно много вставки или добавления, предпочитайте QVector для этого. Конечно, если вы используете QList для хранения разделяемых классов, нет необходимости в дополнительном распределении, что снова делает QList более подходящим.

Итак, предпочитайте QList для большинства случаев:

  • Используя индексы для доступа к отдельным элементам, доступ к элементам будет быстрее, чем QLinkedList
  • Вставка в середину списка потребует перемещения указателей для создания пространства, и это быстрее, чем перемещение фактических данных QVector.
  • Нет необходимости вручную резервировать или изменять размер пространства, так как пустое пространство будет перемещено в конец буфера для последующего использования, а выделение пространства в массиве происходит очень быстро, так как элементы очень маленькие, и это может выделяют много места, не убивая ваше пространство памяти.

Не используйте его в следующих сценариях и предпочитайте QVector:

  • Если вам нужно убедиться, что ваши данные хранятся в ячейках последовательной памяти
  • Если вы редко вставляете данные в случайные позиции, но вы добавляете много данных в конце или в начале, что может вызвать множество ненужных системных вызовов, и вам по-прежнему нужна быстрая индексация.
  • Если вы ищете (общую) замену для простых массивов, которые не будут расти с течением времени.

И, наконец, обратите внимание: QListQVector) имеют функцию reserve(int alloc), которая приведет к росту внутреннего буфера QList, если alloc больше текущего размера внутреннего буфера. Однако это не повлияет на внешний размер QList (size() всегда будет возвращать точное количество элементов, содержащихся в списке).

Ответ 3

Ответ на осел хорош, но он добавит один и тот же объект несколько раз. Вот функции утилиты, которые добавят другой объект для списка интеллектуальных указателей.

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" ), иначе он не будет выполнен.

Ответ 4

Просто используйте что-то вроде

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

По существу, существуют методы to/from Vector/List/Set(), что делает тривиальным изменять размеры контейнеров Qt, когда это необходимо, в несколько ручном, но тривиальном и эффективном (I верьте).

Другое (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(), см.: