Russian Qt Forum
Ноябрь 25, 2024, 04:30 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Контейнеры и куда девается память  (Прочитано 11396 раз)
Lagovas
Гость
« : Август 11, 2012, 16:21 »

В общем вопрос, только вникаю в управление памятью в с++ и пока непонятно пару вопросов, связанных с контейнерами QList и т.п. Расскажите почему и как лучше хранить обьекты в этих контейнерах? В пример возьмем класс Record с тремя полями, для того, что бы он не был равен размером стандартным типам. Как его нужно хранить, в каком списке. QList<Record> или QList<Record*>. И что происходит при QList::insert или QList::append. Не в плане как он добавляет, а в плане памяти при обоих листах. Если QList<Record> и мы туда пишем list.append(Record(x,y,z)), по идее он создаст временный обьект Record, передаст в list.append, а там что произойдет. Произойдет копирование или он возьмет ссылку и будет ее хранить? Но тогда временный обьект удалится сразу после list.append и обьекта не будет. А если копирование, то это затратно при больших обьектах и тогда лучше передавать указатель? Если же создавать через new Record(x,y,z), и потом передавать в QList::append, ибо там требует ссылку, то если копирование, зря обьект создадим...
В общем обьясните пожалуйста что происходит и как лучше. Заранее благодарен.

П.С. потестил немного сам, выяснил что все таки копирует значение QList. Значит стоит делать QList<Record*>, что бы при добавлении оно копировало себе лишь значение указателя, верно? Тогда оно копирует не весь обьект с его полями, а лишь значение указателя, т.е. просто число.
« Последнее редактирование: Август 11, 2012, 16:57 от Lagovas » Записан
fuCtor
Гость
« Ответ #1 : Август 11, 2012, 17:06 »

Если не совсем понятно как работать с памятью в принципе, то лучше разобраться для начала с этим.

Касательно хранения, если класс содержит в себе некоторое количество полей, копирование которых допустимо (не указатели) либо реализован свой конструктор копирования (но время пренебрежимо мало), то можно хранить объектами QList<Record>. К примеру класс/структура точки QPoint удовлетворяет данным требованиями, а вот QWidget уже так нельзя, у него и копирование то запрещено. Такой класс уже по указателю хранить надо => следить за временем жизни объекта и следить чтобы указатели были корректны.

Ссылки нельзя хранить (если ошибаюсь то ткните в man), они ограничены областью видимости функции => хранить можно либо копию объекта либо указатель на объект. В случае QList<Record> хранится копия, удалится временный объект на стеке, если при удалении временного удалится что-то нужное для работы копии это не проблема списка, а проблема программиста.

Тут совсем не понял что подразумевалось:
Цитировать
и потом передавать в QList::append, ибо там требует ссылку, то если копирование, зря обьект создадим...

Если в кратце, то сначала читаем про работу с памятью, как передаются аргументы в функции: по значению, по ссылке, по указателю. А потом возвращаемся к вопросу.

Если объекты тяжелые, то хранить по указателю, НО следить за верностью указателей придется самому, либо заворачивать в один из видов умных указателей (QSharedPointer например). Если объекты маленькие, то можно и копию хранить.
Записан
Lagovas
Гость
« Ответ #2 : Август 11, 2012, 17:16 »

Ясно, спс. У меня Record состоит из QString, int и QDate, хранится таких должно 10 штук, добавляться\удаляться не очень часто. Я так понял можно не заморачиваться и хранить QList<Record>? Просто строки зачастую по ссылке передают, ибо они большими могут быть, думал мб и здесь на всякий случай имеет смысл передавать по ссылке.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Август 11, 2012, 17:28 »

И что происходит при QList::insert или QList::append. Не в плане как он добавляет, а в плане памяти при обоих листах.
QList <T>  держит массив указателей, при добавлении/вставке новый элемент распределяется в куче и указатель на него добавляется в массив, который растет при необходимости. По существу QList <T> делает то же что и QVector <T *> или std::vector <T *>, поэтому делать QList <T *> можно но обычно не нужно.

Однако если размер элемента мал (sizeof(T) <= sizeof(voiid *), напр QList <int>) QList держит просто массив элементов. Важно что в этом случае нельзя опираться на адрес элемента, он становится невалидным при  удалении/вставке. Таким образом Ваш 2-й вариант - практически то же что и первый, ну плюс надо самому удалять.

QList достаточно жирный/расходный. Напр для хранения в QList структуры из 3 int (или 3 float) потребуется 20 байт, а в 64-bits и того больше. А в "просто векторе" это всего 12. Поэтому для хранения большого числа мелких простых данных QList не очень.

Скоростью удаления/вставки QList не блещет, но для хороших структур она может быть намного выше чем у QVector (за счет того что вставляется указатель)

Короче: QList хорош для хранения больших, солидных объектов
« Последнее редактирование: Август 11, 2012, 17:32 от Igors » Записан
Lagovas
Гость
« Ответ #4 : Август 11, 2012, 17:36 »

Цитировать
QList <T>  держит массив указателей, при добавлении/вставке новый элемент распределяется в куче и указатель на него добавляется в массив, который растет при необходимости. По существу QList <T> делает то же что и QVector <T *> или std::vector <T *>, поэтому делать QList <T *> можно но обычно не нужно.
Он хоть и содержит массив указателей, но по идее, он сначала копирует, размещая в куче, и потом хранит указатель. Т.е. лучше создавать QList<T*>, что бы копировался лишь указатель, нежели весь обьект? Ибо если обьект передать, как он его может разместить в куче, не копируя? Ведь передать могли временный обьект какой то.

Т.е. лучше QVector юзать? В общем задача хранить статистику. Т.е. хранить в упорядоченном виде, при добавлении, добавлять сразу упорядоченно. Вроде QVector для этого хуже приспособлен. Хранить по сути должно QString, int и QDate, вроде не емкий обьект, но все же. Что лучше тогда в этом случае, QVector или QList. При добавлении в середину, на сколько я понимаю, QVector должен будет сместить все элементы в сторону. QList же просто изменит 3 (или сколько раз там) указатели.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Август 11, 2012, 17:50 »

Он хоть и содержит массив указателей, но по идее, он сначала копирует, размещая в куче, и потом хранит указатель. Т.е. лучше создавать QList<T*>, что бы копировался лишь указатель, нежели весь обьект? Ибо если обьект передать, как он его может разместить в куче, не копируя? Ведь передать могли временный обьект какой то.
Да, есть расходы на копирование, но обычно они достаточно малы, а при использовании Qt классов (QString, QDate) еще намного меньше за счет имплисит шары. А если Вас это так терзает то можно добавлять и так
Код
C++ (Qt)
theList.push_back(MyStruct());
theList.back().mName = "Name";
theList.back().mDate = ..
 
 

Т.е. лучше QVector юзать? В общем задача хранить статистику. Т.е. хранить в упорядоченном виде, при добавлении, добавлять сразу упорядоченно.
Так здесь никакой не QList и не QVector, а ассоциативный контейнер, напр QMap. Потому что упорядочивание QList/QVector при каждом добавлении обойдется действительно недешево.   
Записан
Lagovas
Гость
« Ответ #6 : Август 11, 2012, 18:03 »

Кстати по поводу имплисит шары, можете вкратце рассказать что о ней нужно знать? Кажется связано со счетчиком ссылок, но непонятно как здесь оно работает, Вкратце пожалуйста.
По поводу Вашего куска кода, он ведь точно так же скопируется. Да, будут значения по умолчанию, но где гарантия что в MyStruct поле name не инициализируется в конструкторе текстом "Война и мир"?
По поводу QMap, как же он здесь подходит, если нужно выводить статистику упорядоченно? Не зря ведь ее хранить нужно упорядоченно. Для этого мб лучше QLinkedList использовать, но думаю для 10 елементов и QList подойдет.
Вопрос просто стоял какой тип для шаблона задать, что бы хранил QList.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Август 11, 2012, 18:39 »

Кстати по поводу имплисит шары, можете вкратце рассказать что о ней нужно знать? Кажется связано со счетчиком ссылок, но непонятно как здесь оно работает, Вкратце пожалуйста.
Код
C++ (Qt)
QString a = "Война";  // строка распределилась в куче
QString b = a;             // a и b используют одну строку (никакой доп памяти не выделено)
...
b = "Мир";           // теперь b ссылается на новую строку
 
По поводу Вашего куска кода, он ведь точно так же скопируется. Да, будут значения по умолчанию, но где гарантия что в MyStruct поле name не инициализируется в конструкторе текстом "Война и мир"?
А зачем писать такой конструктор по умолчанию? Вы хотите иметь все удобства контейнеров и в то же время оптимальный код как на С? Так не получится, надо чем-то поступиться, какие-то накладные расходы всегда есть, это нормально. Вы напрасно уперлись в "ах он копирует" - это место не узкое, до 100K элементов о нем можно вообще не вспоминать. Не стоит полагать что контейнер указателей лучше - new (mallloc) тоже совсем не мгновенный. Выбор контейнера определяется тем какие операции удаления/вставки планируются, как др данные ссылаются на элементы, а совсем не тем "копирует или нет".

По поводу QMap, как же он здесь подходит, если нужно выводить статистику упорядоченно? Не зря ведь ее хранить нужно упорядоченно. Для этого мб лучше QLinkedList использовать, но думаю для 10 елементов и QList подойдет.
Вопрос просто стоял какой тип для шаблона задать, что бы хранил QList.
"Упорядоченно" означает напр "по дате", "по имени", т.е. данные сортированы в определенном порядке. Вероятно Вы хотели сказать наоборот "в том порядке как они (элементы) были добавлены в контейнер" или просто "в порядке поступления".
« Последнее редактирование: Август 11, 2012, 18:41 от Igors » Записан
Lagovas
Гость
« Ответ #8 : Август 11, 2012, 18:53 »

Ясно, спасибо за разъяснение.
В моем случае да, именно упорядочивание, по количеству очков. Если бы в порядке добавления, для этого бы использовал очередь, стек и т.п. (об особенностях структур все таки понятие какое то есть).

И еще вопрос по памяти, если можно. Путаюсь в том, что требуется в аргументе функции, когда запрашивается указатель или ссылка. Не совсем понимаю когда достаточно туда временного обьекта закинуть, что бы он потом сам удалился, ибо мб там копируется все, или нужно выделить в куче обьект и уже его кидать в параметр функции. Есть какие то принятые правила, когда какие аргументы для каких случаев (когда достаточно временного, бо он копируется, когда указатель, и он в функции освободится, когда указатель, но тебе потом нужно самому освобождать и т.п.)? Мб есть что на эту тему почитать...
Просто если передавать std::string, понятно что оно скопируется и мне уже не нужно волноваться за освобождение памяти. Если туда нужно const std::string&, то я не знаю, он будет использовать эту ссылку долго (мб хранить), или просто скопирует значения обьекта, а по ссылке передача происходит, что бы не делать лишних копирований.
Это касается зачастую при создании обьектов различных. Например тот же QList, принимает ссылки, а сам там копирует. В общем если несложно, описать как это в реальной разработке понимается.
Заранее благодарен.
P.S. Вспомнил что больше всего с толку сбивало. QPolygon. По сути QVector<QPoint>. При добавлении требует ссылки на QPoint. И я ломал голову, что туда. Хватит банального &QPoint(0,0) (для примера), или нужно new QPoint(0,0), ибо не знал, копирует он и достаточно временного обьекта, или нужно в кучу кидать, а туда ссылку на него. В итоге остановился на последнем. Но сейчас понимаю, что зря я это сделал, да? Тут по идее утечка потенциальная. Хотя если я правильно понял implicit shared, о чем выше был пример, то мой вариант тоже неплох и не должен приводить к утечке, ибо там скопируется ссылочка на него и все? Но потом как удалять, достаточно будет QPolygon::remove или нет? Еще смущал QPolygon::setPoint, который тоже ссылку принимает, казалось бы для легкого обьекта QPoint. И думай, он там тупо копирнет, или нет, ибо если да, а я туда из кучи ссылку на обьект кину, в куче так и висеть до конца жизни приложения будет та точка...
« Последнее редактирование: Август 11, 2012, 19:13 от Lagovas » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Август 11, 2012, 19:36 »

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

..когда запрашивается указатель или ссылка. Не совсем понимаю когда достаточно туда временного обьекта закинуть,
Код
C++ (Qt)
using std::string;
// В эту ф-цию (MyFunc1) можно подавать как существующий объект, так и временный, который будет разрушен при выходе из  MyFunc1
void MyFunc1( const string  & str );
 
string str;
MyFunc1(str);          
MyFunc1(string("aaa"));            
MyFunc1("aaa");        // тоже можно, есть конструктор  std::string(const char *);        
 

Код
C++ (Qt)
using std::string;
// А в эту только существующий объект
void MyFunc2( string  & str );
 
string str;
MyFunc2(str);          
 
С указателями вообще все однозначно, не понял где Вы там запутались  Непонимающий
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Август 11, 2012, 19:41 »

P.S. Вспомнил что больше всего с толку сбивало. QPolygon. По сути QVector<QPoint>. При добавлении требует ссылки на QPoint. И я ломал голову, что туда. Хватит банального &QPoint(0,0) (для примера), или нужно new QPoint(0,0), ибо не знал, копирует он и достаточно временного обьекта, или нужно в кучу кидать, а туда ссылку на него. В итоге остановился на последнем. Но сейчас понимаю, что зря я это сделал, да? Тут по идее утечка потенциальная.
Да не потенциальная а прямая, кинетическая Улыбающийся Правильно так
Код
C++ (Qt)
polygon.push_back(QPoint(0, 0));
 
// или использовать существующий QPoint, напр
polygon.push_back(polygon2[0]);
 
Записан
Lagovas
Гость
« Ответ #11 : Август 11, 2012, 19:44 »

Не понял, дык утечка или нет?
И опять же, он там сохраняет эту ссылку или копирует? Он вроде не const QPoint& принимает.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Август 11, 2012, 20:00 »

Не понял, дык утечка или нет?
И опять же, он там сохраняет эту ссылку или копирует? Он вроде не const QPoint& принимает.
"дык.. там.. вроде" - звучит мутно, "с пятого на десятое". Предъявите фрагмент кода, тогда обсудим
Записан
Lagovas
Гость
« Ответ #13 : Август 11, 2012, 20:03 »

Вы прокомментировали мое сообщение так
Цитировать
Да не потенциальная а прямая, кинетическая  Правильно так
Вот и уточняю, в том как я делал, добавлял в QPolygon через new QPoint будет утечка или нет. Хотя важнее другое, оно там копирует и можно временный обьект пихать, или сохраняет мой обьект переданный по ссылке?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Август 11, 2012, 20:31 »

Вот и уточняю, в том как я делал, добавлял в QPolygon через new QPoint будет утечка или нет.
Покажите неск строк кода (ржать не буду). А так хз как Вы добавляли через new - оно ж не откомпилится.

Хотя важнее другое, оно там копирует и можно временный обьект пихать, или сохраняет мой обьект переданный по ссылке?
Все контейнеры копируют в себя. Вы можете создать контейнер указателей а сами данные хранить в др месте, но с точки зрения контейнера ничего не меняется - ну просто элемент указатель, значит копируем указатель
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.147 секунд. Запросов: 23.