Название: Контейнеры и куда девается память Отправлено: 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*>, что бы при добавлении оно копировало себе лишь значение указателя, верно? Тогда оно копирует не весь обьект с его полями, а лишь значение указателя, т.е. просто число. Название: Re: Контейнеры и куда девается память Отправлено: fuCtor от Август 11, 2012, 17:06 Если не совсем понятно как работать с памятью в принципе, то лучше разобраться для начала с этим.
Касательно хранения, если класс содержит в себе некоторое количество полей, копирование которых допустимо (не указатели) либо реализован свой конструктор копирования (но время пренебрежимо мало), то можно хранить объектами QList<Record>. К примеру класс/структура точки QPoint удовлетворяет данным требованиями, а вот QWidget уже так нельзя, у него и копирование то запрещено. Такой класс уже по указателю хранить надо => следить за временем жизни объекта и следить чтобы указатели были корректны. Ссылки нельзя хранить (если ошибаюсь то ткните в man), они ограничены областью видимости функции => хранить можно либо копию объекта либо указатель на объект. В случае QList<Record> хранится копия, удалится временный объект на стеке, если при удалении временного удалится что-то нужное для работы копии это не проблема списка, а проблема программиста. Тут совсем не понял что подразумевалось: Цитировать и потом передавать в QList::append, ибо там требует ссылку, то если копирование, зря обьект создадим... Если в кратце, то сначала читаем про работу с памятью, как передаются аргументы в функции: по значению, по ссылке, по указателю. А потом возвращаемся к вопросу. Если объекты тяжелые, то хранить по указателю, НО следить за верностью указателей придется самому, либо заворачивать в один из видов умных указателей (QSharedPointer например). Если объекты маленькие, то можно и копию хранить. Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 11, 2012, 17:16 Ясно, спс. У меня Record состоит из QString, int и QDate, хранится таких должно 10 штук, добавляться\удаляться не очень часто. Я так понял можно не заморачиваться и хранить QList<Record>? Просто строки зачастую по ссылке передают, ибо они большими могут быть, думал мб и здесь на всякий случай имеет смысл передавать по ссылке.
Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 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 хорош для хранения больших, солидных объектов Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 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 (или сколько раз там) указатели. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 11, 2012, 17:50 Он хоть и содержит массив указателей, но по идее, он сначала копирует, размещая в куче, и потом хранит указатель. Т.е. лучше создавать QList<T*>, что бы копировался лишь указатель, нежели весь обьект? Ибо если обьект передать, как он его может разместить в куче, не копируя? Ведь передать могли временный обьект какой то. Да, есть расходы на копирование, но обычно они достаточно малы, а при использовании Qt классов (QString, QDate) еще намного меньше за счет имплисит шары. А если Вас это так терзает то можно добавлять и такКод
Т.е. лучше QVector юзать? В общем задача хранить статистику. Т.е. хранить в упорядоченном виде, при добавлении, добавлять сразу упорядоченно. Так здесь никакой не QList и не QVector, а ассоциативный контейнер, напр QMap. Потому что упорядочивание QList/QVector при каждом добавлении обойдется действительно недешево. Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 11, 2012, 18:03 Кстати по поводу имплисит шары, можете вкратце рассказать что о ней нужно знать? Кажется связано со счетчиком ссылок, но непонятно как здесь оно работает, Вкратце пожалуйста.
По поводу Вашего куска кода, он ведь точно так же скопируется. Да, будут значения по умолчанию, но где гарантия что в MyStruct поле name не инициализируется в конструкторе текстом "Война и мир"? По поводу QMap, как же он здесь подходит, если нужно выводить статистику упорядоченно? Не зря ведь ее хранить нужно упорядоченно. Для этого мб лучше QLinkedList использовать, но думаю для 10 елементов и QList подойдет. Вопрос просто стоял какой тип для шаблона задать, что бы хранил QList. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 11, 2012, 18:39 Кстати по поводу имплисит шары, можете вкратце рассказать что о ней нужно знать? Кажется связано со счетчиком ссылок, но непонятно как здесь оно работает, Вкратце пожалуйста. Код
По поводу Вашего куска кода, он ведь точно так же скопируется. Да, будут значения по умолчанию, но где гарантия что в MyStruct поле name не инициализируется в конструкторе текстом "Война и мир"? А зачем писать такой конструктор по умолчанию? Вы хотите иметь все удобства контейнеров и в то же время оптимальный код как на С? Так не получится, надо чем-то поступиться, какие-то накладные расходы всегда есть, это нормально. Вы напрасно уперлись в "ах он копирует" - это место не узкое, до 100K элементов о нем можно вообще не вспоминать. Не стоит полагать что контейнер указателей лучше - new (mallloc) тоже совсем не мгновенный. Выбор контейнера определяется тем какие операции удаления/вставки планируются, как др данные ссылаются на элементы, а совсем не тем "копирует или нет".По поводу QMap, как же он здесь подходит, если нужно выводить статистику упорядоченно? Не зря ведь ее хранить нужно упорядоченно. Для этого мб лучше QLinkedList использовать, но думаю для 10 елементов и QList подойдет. "Упорядоченно" означает напр "по дате", "по имени", т.е. данные сортированы в определенном порядке. Вероятно Вы хотели сказать наоборот "в том порядке как они (элементы) были добавлены в контейнер" или просто "в порядке поступления".Вопрос просто стоял какой тип для шаблона задать, что бы хранил QList. Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 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. И думай, он там тупо копирнет, или нет, ибо если да, а я туда из кучи ссылку на обьект кину, в куче так и висеть до конца жизни приложения будет та точка... Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 11, 2012, 19:36 В моем случае да, именно упорядочивание, по количеству очков. Если бы в порядке добавления, для этого бы использовал очередь, стек и т.п. (об особенностях структур все таки понятие какое то есть). Если упорядочивание одноразовое (или редкое), то можно обойтись QList, иначе нужен ассоциативный контейнер..когда запрашивается указатель или ссылка. Не совсем понимаю когда достаточно туда временного обьекта закинуть, Код
Код С указателями вообще все однозначно, не понял где Вы там запутались ??? Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 11, 2012, 19:41 P.S. Вспомнил что больше всего с толку сбивало. QPolygon. По сути QVector<QPoint>. При добавлении требует ссылки на QPoint. И я ломал голову, что туда. Хватит банального &QPoint(0,0) (для примера), или нужно new QPoint(0,0), ибо не знал, копирует он и достаточно временного обьекта, или нужно в кучу кидать, а туда ссылку на него. В итоге остановился на последнем. Но сейчас понимаю, что зря я это сделал, да? Тут по идее утечка потенциальная. Да не потенциальная а прямая, кинетическая :) Правильно такКод
Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 11, 2012, 19:44 Не понял, дык утечка или нет?
И опять же, он там сохраняет эту ссылку или копирует? Он вроде не const QPoint& принимает. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 11, 2012, 20:00 Не понял, дык утечка или нет? "дык.. там.. вроде" - звучит мутно, "с пятого на десятое". Предъявите фрагмент кода, тогда обсудимИ опять же, он там сохраняет эту ссылку или копирует? Он вроде не const QPoint& принимает. Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 11, 2012, 20:03 Вы прокомментировали мое сообщение так
Цитировать Да не потенциальная а прямая, кинетическая Правильно так Вот и уточняю, в том как я делал, добавлял в QPolygon через new QPoint будет утечка или нет. Хотя важнее другое, оно там копирует и можно временный обьект пихать, или сохраняет мой обьект переданный по ссылке?Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 11, 2012, 20:31 Вот и уточняю, в том как я делал, добавлял в QPolygon через new QPoint будет утечка или нет. Покажите неск строк кода (ржать не буду). А так хз как Вы добавляли через new - оно ж не откомпилится.Хотя важнее другое, оно там копирует и можно временный обьект пихать, или сохраняет мой обьект переданный по ссылке? Все контейнеры копируют в себя. Вы можете создать контейнер указателей а сами данные хранить в др месте, но с точки зрения контейнера ничего не меняется - ну просто элемент указатель, значит копируем указательНазвание: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 11, 2012, 21:17 Не, уже не покажу... Поправил оказывается давно. А добавлял типа так вроде,
Цитировать QPoint* p = new Point(x,y); Если не лень и будет время, просмотрите код который сделал. Игра змейка, интересны моменты по утечкам, ну и замечания по другому быдлокоду. Ибо игра писалась не ради игры, а ради практики работы с памятью. Два основных класса game и snake. Еще один отвечает за отрисовку, но там вроде ничего сложного.varPolygon.append(*p); П.С. Еще вопрос напоследок, если в классе обьявить поле SomeClass c; Память выделиться под эту переменную сразу через конструктор по умолчанию или нет? Или только если в конструкторе класса где обьявляем поле напишем c=SomeClass()? Смотрел в книгах, там часто обьявляют в классе простые классы, где нету конструкторов с аргументами, что бы приметить как они и где инициализируются. Знаю что такое поле освободится само после удаления обьекта которому принадлежит, в отличие от случая, если обьявить SomeClass*. Из-за неуверенности того как работает, в своем коде обьявлял в основном через указатели, хотя если через обычное обьявление, оно упростит управление памятью... Snake.h Код: #ifndef SNAKE_H Код: #include "snake.h" Код: #ifndef GAME_H Код: #include "game.h" Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 12, 2012, 10:49 Не, уже не покажу... Поправил оказывается давно. А добавлял типа так вроде, Так просто утечка, 2 копии данных, одна создана new, др. скопирована в контейнер. Первая не удаляется. Цитировать QPoint* p = new Point(x,y); varPolygon.append(*p); П.С. Еще вопрос напоследок, если в классе обьявить поле SomeClass c; Память выделиться под эту переменную сразу через конструктор по умолчанию или нет? Память для объекта и всех его членов (и членов членов) выделяется сразу и еще до того как управление передается конструктору. Размер этой памяти = sizeof(A). Если не лень и будет время, просмотрите код который сделал. Игра змейка, интересны моменты по утечкам, ну и замечания по другому быдлокоду. Посмотрю когда буду посвободнее Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 12, 2012, 11:07 Цитировать Память для объекта и всех его членов (и членов членов) выделяется сразу и еще до того как управление передается конструктору. Размер этой памяти = sizeof(A). А как тогда создать так обьект через конструктор с параметрами? Если писатьКод: class A{ И еще маленький такой вопрос, когда требуется ссылка в аргументе, как правильно сделать вроде такого: Код: void foo(QString&); Заранее благодарен. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 12, 2012, 12:19 А как тогда создать так обьект через конструктор с параметрами? Если писать ПримерыКод: class A{ Код
И еще маленький такой вопрос, когда требуется ссылка в аргументе, как правильно сделать вроде такого: Это уже проходили выше, нужна константная ссылкаКод: void foo(QString&); Заранее благодарен. Код
Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 12, 2012, 12:32 Код: class MyClass { Код: class MyClass2{ Код: class MyClass Заранее благодарен. П.С. Спасибо большое, уже стало яснее с параметрами. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 12, 2012, 13:21 То ли я дурак, то ли Вы опечатались где то. Во втором классе должно быть по ходу Не вижу опечатки, Вы просто поменяли имя членаКод: class MyClass2{ Значит если поле обьект и его нужно создавать не через стандартный конструктор, то нужно инициализировать через аргументы конструктора, и никак иначе? Если Вы не указываете никаких конструкторов, то будет создан по умолчанию. Однако если Вы написали конструктор(ы) с параметром, то конструктор без параметров не создается. Такой объект нельзя будет поместить в контейнер. Такой объект может быть членом до класса, но тот в своем конструкторе обязан будет вызвать конструктор этого члена с параметром. Смысл простой - невозможно создать объект минуя конструктор (ну почти), поэтому если конструктор с параметрами - он вызывается при создании.Конструктор с параметрами по умолчанию (всеми) считается конструктором по умолчанию, это удобно и часто используется. Если член имеет 2 или более конструктора, то в конструкторе вмещающего класса можно указать любой из них. Если ничего не указано, член инициализируется его конструктором по умолчанию. Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 12, 2012, 13:43 Что будет в этом случае?
Код: class Foo Код: Foo2():foo(QString("foo")){...} Заранее благодарен. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 12, 2012, 14:14 Правильно инициализировать член не в теле конструктора, а в списке инициализаторов (после двоеточия)
Код
Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 12, 2012, 14:19 Это я понял, я лишь уточняю, по другому никак? Просто если там у Foo будет конструктор типа (HardObject&, HardObject2&...) которые тоже инициализируется различными аргументами то получится что то вроде :foo(HardObject(Param1(), Param2()), HardObject2(...)), и что бы такого бардака не было, в таком случае уже лучше указатель делать членом класса, да?
Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 12, 2012, 15:53 Это я понял, я лишь уточняю, по другому никак? Просто если там у Foo будет конструктор типа (HardObject&, HardObject2&...) которые тоже инициализируется различными аргументами то получится что то вроде :foo(HardObject(Param1(), Param2()), HardObject2(...)), и что бы такого бардака не было, в таком случае уже лучше указатель делать членом класса, да? Нет, использовать член-указатель или член-объект определяется др соображениями. Просто сделайте у классов-членов конструкторы по умолчанию - и тогда уже можно и в теле конструктора ими заниматьсяНазвание: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 14, 2012, 01:06 Спасибо. Начал читать по поводу ссылок, передачу как аргументов и т.п. и назрел вопрос.
Код: void f(Type* t){}; Из-за чего вопрос. Ведь если функция принимает const type&, то можно передать временный обьект, его ссылка скопируется (только ссылка, не сам обьект), он удалится и ссылка указывает на пустое место? В целом в чем заключается вопрос. Какая область видимости временного обьекта передаваемого как параметр? До конца функции или до ")"? Искал в Эккеле, страуструпе... не нашел что бы черным по белому рассказывалось, что живут вот до сюда. По логике конечно живут до конца функции, но все же из-за того что я новенький в плюсах, не уверен пока более опытные не подтвердят. Сам попробовал проверить это с помощью простого кода, все вроде сходится. Кстати вопрос по коду еще: Код: #include <QtCore/QCoreApplication> Заранее благодарен. Название: Re: Контейнеры и куда девается память Отправлено: Igors от Август 14, 2012, 02:16 Т.е. после вызова new Type() мы получаем временный обьект Type*. Его значение (ссылка на обьект в куче) копируется в локальную переменную t функции f (действующую в ее области видимости), после этого что происходит - этот временный указатель возвращенный через new удаляется из-за выхода из области или нет? Никакой ссылкой в приведенном Вами коде и не пахнет. Вообще "ссылка" - это условное понятие, "физически" никаких ссылок нет, есть структуры и их адреса - все. Вот от этой печки и пляшите.Код Создали в куче объект, new вернул указатель на него (адрес). Грубо: после выполнения new адрес сидит в регистре eax. Толкнули этот адрес на стек (push eax) и вызвали foo (call [foo]). Ф-ция отработала и вернула управление. Вероятно подровняли стек (add, sp, 4) и все, никаких удалений не производится. Если ф-ция объект не удалила, он так в куче и будет. Теперь пресловутая ссылка Код На стеке отвели место для безымянного Record и вызвали его конструктор. Возможно конструктор там какие-то члены создал в куче - то его дело. Толкнули опять-таки адрес Record на стек и вызвали ф-цию. Ф-ция отработала и вернула упрвление. Вызываем деструктор Record (если есть). Выравниваем стек. Готовченко. Что неясно? Название: Re: Контейнеры и куда девается память Отправлено: Lagovas от Август 14, 2012, 08:16 Извиняюсь, в первом коде должно было быть const Type&, 2 часа ночи дали о себе знать...
Код: На стеке отвели место для безымянного Record и вызвали его конструктор. Возможно конструктор там какие-то члены создал в куче - то его дело. Толкнули опять-таки адрес Record на стек и вызвали ф-цию. Ф-ция отработала и вернула упрвление. Вызываем деструктор Record (если есть). Выравниваем стек. Готовченко. |