Порядок следования дочерних объектов QObject (стратегический вопрос)
Вопрос
Для одного из моих проектов у меня есть дерево производных объектов QObject, которые используют родительскую / дочернюю функциональность QObject для построения дерева.
Это очень полезно, поскольку я использую сигналы и слоты, использую защищенные указатели Qt и ожидаю, что родительские объекты удалят дочерние объекты при их удалении.
Пока все идет хорошо.К сожалению, сейчас мой проект требует, чтобы я управлял / изменял порядок дочерних элементов.QObject не предоставляет никаких средств для изменения порядка своих дочерних элементов (исключение:Функция raise() QWidget - но в данном случае это бесполезно). Итак, теперь я ищу стратегию контроля за порядком следования детей. У меня было несколько идей, но я не уверен в их плюсах и минусах:
Вариант А: Пользовательская переменная-член индекса сортировки
Используйте int m_orderIndex
переменная-член в качестве ключа сортировки и предоставляет sortedChildren()
метод, который возвращает список QObjects, отсортированных по этому ключу.
- Легко внедряется в существующую структуру объекта.
- Проблематично, когда
QObject::children()
метод переопределен - приведет к проблемам во время циклов при изменении порядка элементов, также является более дорогостоящим, чем реализация по умолчанию. - Следует вернуться к порядку объектов QObject, если все ключи сортировки равны или 0 / по умолчанию.
Вариант В: Избыточный список дочерних элементов
Поддерживать избыточный список дочерних элементов в QList
, и добавлять к нему дочерние элементы, когда они создаются и уничтожаются.
- Требуется дорогостоящее отслеживание добавленных / удаленных объектов.Это в основном приводит ко второму дочернему / родительскому отслеживанию и множеству сигналов / слотов.QObject уже выполняет все это внутренне, так что, возможно, не стоит делать это снова.Также кажется, что для такой простой вещи, как изменение порядка следования дочерних элементов, добавляется много наворотов.
- Хорошая гибкость, поскольку QList дочерних элементов может быть изменен по мере необходимости.
- Позволяет дочернему элементу находиться в QList более одного раза или не находиться вообще (даже если он все еще может быть дочерним элементом QObject)
Вариант С:...?
Любые идеи или отзывы, особенно от людей, которые уже решили эту проблему в своих собственных проектах, высоко ценятся.С наступающим новым годом!
Решение
В последние дни я потратил много времени на рассмотрение всех этих вариантов и тщательно обсуждал их с некоторыми другими программистами.Мы решили пойти на Вариант А.
Каждый из объектов, которыми мы управляем, является дочерним по отношению к родительскому объекту.Поскольку Qt не предоставляет никаких средств изменения порядка этих объектов, мы решили добавить int m_orderIndex
свойство для каждого объекта, которое по умолчанию равно 0.
Каждый объект имеет функцию доступа sortedChildren()
который возвращает QObjectList
детей.Что мы делаем в этой функции:
- Используйте обычный
QObject::chilren()
функция для получения списка всех доступных дочерних объектов. dynamic_cast
все объекты в наш «базовый класс», который обеспечиваетm_orderIndex
свойство.- Если объект можно приводить, добавьте его во временный список объектов.
- использовать
qSort
с обычаемLessThan
функция, чтобы узнать, нужно ли qSort изменить порядок двух объектов. - Вернуть список временных объектов.
Мы сделали это по следующим причинам:
- Существующий код (особенно собственный код Qt) можно продолжать использовать.
children()
не беспокоясь о побочных эффектах. - Мы можем использовать обычный
children()
функционировать там, где порядок не имеет значения, без потери производительности. - В тех местах, где нам нужен упорядоченный список детей, просто заменяем
children()
кsortedChildren()
и получить желаемый эффект.
Одно из преимуществ этого подхода заключается в том, что порядок дочерних элементов не меняется, если все индексы сортировки установлены в ноль.
Извините, что отвечаю на мой собственный вопрос, надеюсь, что это просветит людей с той же проблемой.;)
Другие советы
А как насчет чего-то вроде...
- QList listChildren = (QList)дети();
- сортировать списокДети
- foreach listChildren setParent( TempParent )
- foreach listChildren setParent( OriginalParent )
Неприятный хак: QObject::дети() возвращает ссылку на константу.Вы можете отказаться от константности и, таким образом, напрямую манипулировать внутренним списком.
Однако это довольно зло и может привести к признанию недействительными итераторов, которые QObject хранит внутри себя.
У меня нет отдельного варианта C, но, сравнивая варианты A и B, вы говорите ~4 байта (32-битный указатель, 32-битное целое число) в любом случае, поэтому я бы выбрал вариант B, так как вы можете сохранить этот список отсортирован.
Чтобы избежать дополнительных сложностей с отслеживанием детей, вы можете обмануть и сохранить список отсортированным и аккуратным, но объединить его с методом sortedChildren, который отфильтровывает все недетские элементы.С точки зрения сложности, это должно привести к O(nlogm) (n = дочерние элементы, m = записи списка, предполагая, что m >= n, т.е.дети всегда добавляются), если только у вас нет больших изменений в отношении детей.Назовем этот вариант С.
Быстрая сортировка в предложенном вами варианте A дает вам O(n2) (wc), но также требует, чтобы вы получали указатели, отслеживали их, получали целое число и т. д.Комбинированному методу нужен только список указателей (не более O(n)).
У меня была такая же проблема, и я решил ее с помощью варианта B.Отслеживание не так уж сложно, просто создайте метод "void addChild(тип *ptr);" и еще один для удаления дочернего элемента.
Вы не страдаете от злонамеренной избыточности, если вы храните дочерние элементы исключительно в частном / общедоступном списке дочерних элементов (QList) каждого элемента и удаляете базу QObject.На самом деле довольно легко реализовать автоматическое освобождение от дочерних элементов в free (хотя для этого требуется дополнительный родительский указатель).