Russian Qt Forum

Qt => Вопросы новичков => Тема начата: GrieVeR-13 от Апрель 03, 2015, 11:17



Название: Как правильно работать с контейнером QList?
Отправлено: GrieVeR-13 от Апрель 03, 2015, 11:17
Здравствуйте.
Сегодня натолкнулся на неприятную проблему.
В моей программе в объекте класса
Код:
QList<MyClass> myClassList
хранится около 15к объектов.
В некоторый момент времени мне понадобилось хранить указатель на один из объектов в листе
Код:
MyClass *ptr = &myClassList[0]
Обнаружил, что благодаря функции detach() класса QList, по неизвестному мне условию, всем объектам внутри контейнера выделяется новая область памяти, вследствие чего указатель начинает ссылаться на другой объект.
Условие внутри detach() выполняется, когда я вызываю функцию вида
Код:
computeFunc(GetFunc(myClassList));
int GetFunc(QList<MyClass> myClassList) { .. }
т.е. когда производится запуск конструктора копирования. И только в таком виде. Если поместить результат GetFunc в промежуточную переменную или передать myClassList по ссылке, проблем не возникает.
Вопрос: я неверно работаю с контейнером? Например, может положено хранить в листе только ссылки?


Название: Re: Как правильно работать с контейнером QList?
Отправлено: Пантер от Апрель 03, 2015, 11:20
Передавай контейнер по константной ссылке. Зачем его копировать?


Название: Re: Как правильно работать с контейнером QList?
Отправлено: __Heaven__ от Апрель 03, 2015, 11:22
Фактически копирования не происходит до первого модифицирования контейнера
Цитировать
QList::​QList(const QList<T> & other)
Constructs a copy of other.

This operation takes constant time, because QList is implicitly shared. This makes returning a QList from a function very fast. If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: Old от Апрель 03, 2015, 11:24
Передавай контейнер по константной ссылке. Зачем его копировать?
Перераспределение может произойти и при простом добавлении элементов.

2 GrieVeR-13
Не храните в списке значения, храните указатели (желательно умные) на значения. Тогда, даже если список перераспределиться, указатели на значения останутся прежними.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: __Heaven__ от Апрель 03, 2015, 11:40
Перераспределение может произойти и при простом добавлении элементов.

Почему так может произойти? Для std::list я видел схему из * next * prev. В qt механизм хитрее?


Название: Re: Как правильно работать с контейнером QList?
Отправлено: GrieVeR-13 от Апрель 03, 2015, 11:49
Old: даже при добавлении? Странно, что ранее никогда не сталкивался с таким явлением.
Тем не менее, все равно непонятно почему перераспределения не происходит, если не вкладывать GetFunc в computeFunc.
Код:
int i = GetFunc(myClassList);
computeFunc(i);
Хочу уточнить, что указатель ссылается на объект исходного контейнера, а не на его копию, если вдруг кто-то меня не так понял. Обновление памяти происходит при первом же обращении к функции operator[] после вызова computeFunc.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: Авварон от Апрель 03, 2015, 12:00
Почему так может произойти? Для std::list я видел схему из * next * prev. В qt механизм хитрее?

Табличка в начале вам поможет https://marcmutz.wordpress.com/effective-qt/containers/


Название: Re: Как правильно работать с контейнером QList?
Отправлено: qate от Апрель 03, 2015, 12:14
В некоторый момент времени мне понадобилось хранить указатель на один из объектов в листе
Код:
MyClass *ptr = &myClassList[0]

храни индекс элемента, а не указатель


Название: Re: Как правильно работать с контейнером QList?
Отправлено: Igors от Апрель 03, 2015, 12:54
Код:
computeFunc(GetFunc(myClassList));
int GetFunc(QList<MyClass> myClassList) { .. }
т.е. когда производится запуск конструктора копирования. И только в таком виде. Если поместить результат GetFunc в промежуточную переменную или передать myClassList по ссылке, проблем не возникает.
Вопрос: я неверно работаю с контейнером? Например, может положено хранить в листе только ссылки?
Ну Вы же передали копию - он Вам указатель на скопированный элемент и вернул. Все корректно с точки зрения языка. А когда он делал эту копию (сразу или после) - его личное дело. Зачем контейнер по значению передавать? Лень было писать const & - ну получайте проблемы. И QList здесь ни при чем - с любым контейнером то же самое


Название: Re: Как правильно работать с контейнером QList?
Отправлено: GrieVeR-13 от Апрель 03, 2015, 14:19
..Ну Вы же передали копию - он Вам указатель на скопированный элемент и вернул. Все корректно с точки зрения языка. А когда он делал эту копию (сразу или после) - его личное дело. Зачем контейнер по значению передавать? Лень было писать const & - ну получайте проблемы. И QList здесь ни при чем - с любым контейнером то же самое
Я чуть выше добавил, что указатель - на оригинал. Фокус именно в реализации контейнера (см. функцию detach()).
Константную ссылку написать забыл. Заинтересовала скорее возникшая проблема, чем метод решения. Всегда считал, что происходит разовая инициализация объекта в листе.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: __Heaven__ от Апрель 03, 2015, 14:26
operator[] возвращает неконстантную ссылку на элемент контейнера, что подразумевает дальнейшую возможность его редактирования, что в свою очередь вызывает detach() для дублирования.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: GrieVeR-13 от Апрель 03, 2015, 15:31
operator[] возвращает неконстантную ссылку на элемент контейнера, что подразумевает дальнейшую возможность его редактирования, что в свою очередь вызывает detach() для дублирования.
Да, именно такая ссылка мне и нужна, остается не ясным, с какой целью при определенных условиях дублировать весь список.
Всем большая благодарность за оперативные ответы.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: Igors от Апрель 03, 2015, 16:36
...остается не ясным, с какой целью при определенных условиях дублировать весь список.
Так работает implicit sharing в Qt. Копирование происходит когда возникает необходимость.


Название: Re: Как правильно работать с контейнером QList?
Отправлено: lit-uriy от Апрель 03, 2015, 20:20
GrieVeR-13, рекомендую почитать ПОЛНОСТЬЮ, документацию времён Qt4.6, на русском:
http://www.doc.crossplatform.ru/qt/4.6.x/containers.html

особо обратите внимание на раздел об итераторах, там много тонких мест указано, ну и разумеется раздел "Стратегии увеличения размера"