Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Lagovas от Август 11, 2012, 16:21



Название: Контейнеры и куда девается память
Отправлено: 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) еще намного меньше за счет имплисит шары. А если Вас это так терзает то можно добавлять и так
Код
C++ (Qt)
theList.push_back(MyStruct());
theList.back().mName = "Name";
theList.back().mDate = ..
 
 

Т.е. лучше 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
Кстати по поводу имплисит шары, можете вкратце рассказать что о ней нужно знать? Кажется связано со счетчиком ссылок, но непонятно как здесь оно работает, Вкратце пожалуйста.
Код
C++ (Qt)
QString a = "Война";  // строка распределилась в куче
QString b = a;             // a и b используют одну строку (никакой доп памяти не выделено)
...
b = "Мир";           // теперь b ссылается на новую строку
 
По поводу Вашего куска кода, он ведь точно так же скопируется. Да, будут значения по умолчанию, но где гарантия что в 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, иначе нужен ассоциативный контейнер

..когда запрашивается указатель или ссылка. Не совсем понимаю когда достаточно туда временного обьекта закинуть,
Код
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);          
 
С указателями вообще все однозначно, не понял где Вы там запутались  ???


Название: Re: Контейнеры и куда девается память
Отправлено: Igors от Август 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]);
 


Название: 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);
varPolygon.append(*p);
Если не лень и будет время, просмотрите код который сделал. Игра змейка, интересны моменты по утечкам, ну и замечания по другому быдлокоду. Ибо игра писалась не ради игры, а ради практики работы с памятью. Два основных класса game и snake. Еще один отвечает за отрисовку, но там вроде ничего сложного.
П.С. Еще вопрос напоследок, если в классе обьявить поле SomeClass c; Память выделиться под эту переменную сразу через конструктор по умолчанию или нет? Или только если в конструкторе класса где обьявляем поле напишем
c=SomeClass()? Смотрел в книгах, там часто обьявляют в классе простые классы, где нету конструкторов с аргументами, что бы приметить как они и где инициализируются. Знаю что такое поле освободится само после удаления обьекта которому принадлежит, в отличие от случая, если обьявить SomeClass*.
Из-за неуверенности того как работает, в своем коде обьявлял в основном через указатели, хотя если через обычное обьявление, оно упростит управление памятью...
Snake.h
Код:
#ifndef SNAKE_H
#define SNAKE_H
#include <QObject>
#include <QQueue>
#include <QPoint>
class QPolygon;

class Snake : public QObject
{
    Q_OBJECT

public:
    enum Direction {Left, Right, Top, Bottom};
    Snake();
    ~Snake();

    QPoint getTail();
    QPoint getHead();
    void setHead(QPoint&);
    int getCountBlocks();
    int getLength();
    const QPolygon* getBlockPositions();
    Direction getDirection();

    void Reset();
    void MoveToLeft();
    void MoveToRight();
    void MoveToTop();
    void MoveToBottom();
    bool Move();
    void AddBlock();
    void ChangeDirection(Direction);

signals:
    void onBlockAddFinish(QPoint);


private:
    QQueue<QPoint>* addedBlocks;
    QPolygon* blockPositions;
    Direction direction;
    void MoveBlocks();
    void CreateStandartSnake();
    bool isCollisionOfSnake();
    void Initialize();
};

#endif // SNAKE_H
Snake.cpp
Код:
#include "snake.h"
#include <QQueue>
#include <QPoint>
#include <QPolygon>
#include <QMessageBox>

Snake::Snake()
{
    Initialize();
}

void Snake::Initialize()
{
    addedBlocks = new QQueue<QPoint>();
    blockPositions = new QPolygon();
    CreateStandartSnake();
    direction = Right;
}

Snake::~Snake()
{
    delete addedBlocks;
    delete blockPositions;
}

void Snake::MoveBlocks()
{
    bool newBlock = false;
    //time to add new block to snake
    if(addedBlocks->count() > 0 && getTail() == addedBlocks->head())
    {
        newBlock = true;
    }

    for(int i = getLength() - 1; i != 0; i--)
    {
        blockPositions->setPoint(i, blockPositions->point(i-1));
    }

    if (newBlock)
    {
        blockPositions->append(addedBlocks->dequeue());
        emit this->onBlockAddFinish(getTail());
    }
}

void Snake::Reset()
{
    blockPositions->clear();
    addedBlocks->clear();
    Initialize();
}

void Snake::CreateStandartSnake()
{
    blockPositions->append(QPoint(2,0));
    blockPositions->append(QPoint(1,0));
    blockPositions->append(QPoint(0,0));
}

bool Snake::Move()
{
    MoveBlocks();
    switch (direction)
    {
    case Left:
        MoveToLeft();
        break;
    case Right:
        MoveToRight();
        break;
    case Bottom:
        MoveToBottom();
        break;
    case Top:
        MoveToTop();
        break;
    }
    return isCollisionOfSnake();
}

bool Snake::isCollisionOfSnake()
{
    QPoint head = getHead();
    for (int i = 1; i < getLength(); i++)
    {
        if (head == blockPositions->point(i))
        {
            return false;
        }
    }
    return true;
}

void Snake::ChangeDirection(Direction direct)
{
    if ((direction == Left && direct == Right) || (direction == Right && direct == Left))
    {
        return;
    }
    if ((direction == Top && direct == Bottom) || (direction == Bottom && direct == Top))
    {
        return;
    }
    direction = direct;
}

void Snake::setHead(QPoint &point)
{
    blockPositions->setPoint(0,point);
}

void Snake::AddBlock()
{
    addedBlocks->enqueue(getHead());
}

void Snake::MoveToLeft()
{
    QPoint head = getHead();
    head.rx()--;
    blockPositions->setPoint(0,head);
}

void Snake::MoveToRight()
{
    QPoint head = getHead();
    head.rx()++;
    blockPositions->setPoint(0,head);
}

void Snake::MoveToTop()
{
    QPoint head = getHead();
    head.ry()--;
    blockPositions->setPoint(0,head);
}

void Snake::MoveToBottom()
{
    QPoint head = getHead();
    head.ry()++;
    blockPositions->setPoint(0, head);
}

QPoint Snake::getTail()
{
    return blockPositions->point(getLength()-1);
}

QPoint Snake::getHead()
{
    return blockPositions->point(0);
}

int Snake::getCountBlocks()
{
    return blockPositions->count() + addedBlocks->count();
}

int Snake::getLength()
{
    return blockPositions->count();
}

const QPolygon* Snake::getBlockPositions()
{
    return blockPositions;
}

Snake::Direction Snake::getDirection()
{
    return direction;
}
Game.h
Код:
#ifndef GAME_H
#define GAME_H
#include <QObject>
#include "snake.h"
#include "IBlockDraw.h"
#include <QQueue>
#include <QKeyEvent>
#include <QBrush>
#include "statistic.h"
class QPoint;
class QTimer;

class Game : public QObject
{
    Q_OBJECT
public:
    enum Speed {Slow = 200, Medium = 100, Fast = 50};
    Game(int width, int height, IBlockDraw* BlockDraw);
    ~Game();
    int getPoints();
    void Start();
    void End();
    void setSnakeColor(const QBrush&);
    void setBlockColor(const QBrush&);
    void setBackgroundColor(const QBrush&);
    Statistic* getStatistic();
    void KeyPressHandle(QKeyEvent *);
    Speed getSpeed();
    void setSpeed(Speed);
    Snake* snake;
    void SaveResult(QString&);

protected:
    bool isNewBlock();
    void AddNewBlock();

private:
    int points;
    QTimer* moveTimer;
    QQueue<Snake::Direction>* pressedDirections;
    QPoint* block;
    QBrush* backgroundColor;
    QBrush* snakeColor;
    QBrush* blockColor;
    IBlockDraw* blockDraw;
    Statistic statistic;
    int eatPoints;
    int width;
    int height;
    Speed speed;

    bool Move();
    void NewBlock();
    void ClearBlock(const QPoint&);
    void DrawSnakeBlock(const QPoint&);
    void DrawBlock();
    void Draw(const QPoint&, const QBrush&);
    void DrawSnake();
    void DrawBackground();
    void setPoints(int points);
    void AddPoints(int);
    void CheckSnakeOutOfMap();

public slots:
    void MoveByTime();
    void AddSnakeBlockFinish(const QPoint&);


signals:
    void onPointsChanged(int points);
    void onGameOver();
    void onSnakeBlockCountChanged(int legth);

};

#endif // GAME_H
Game.cpp
Код:
#include "game.h"
#include <QPoint>
#include <QTimer>
#include <QBrush>
#include <QQueue>
#include "snake.h"
#include <QDateTime>


Game::Game(int width, int height, IBlockDraw *BlockDraw)
{
    QString path("statistic.db");
    statistic = Statistic(path);
    eatPoints = 100;
    this->width = width;
    this->height = height;
    blockDraw = BlockDraw;
    moveTimer = new QTimer(this);
    moveTimer->setInterval(Slow);
    moveTimer->stop();
    snake = new Snake();
    backgroundColor = new QBrush(Qt::gray);
    snakeColor = new QBrush(Qt::blue);
    blockColor = new QBrush(Qt::red);
    pressedDirections = new QQueue<Snake::Direction>();
    QObject::connect(moveTimer, SIGNAL(timeout()), this, SLOT(MoveByTime()));
    QObject::connect(snake, SIGNAL(onBlockAddFinish(QPoint)), this, SLOT(AddSnakeBlockFinish(const QPoint&)));
}

Game::~Game()
{
    delete snake;
    delete moveTimer;
    delete pressedDirections;
    delete block;
    delete backgroundColor;
    delete blockColor;
    delete snakeColor;
    delete blockDraw;
}

void Game::Start()
{
    snake->Reset();
    DrawBackground();
    DrawSnake();
    points = 0;
    moveTimer->start();
    //emit onSnakeBlockCountChanged(snake->getCountBlocks());
    NewBlock();
}

void Game::End()
{
    moveTimer->stop();
    //clearing old directions for new game
    pressedDirections->clear();
    pressedDirections = new QQueue<Snake::Direction>();
    emit onGameOver();
}

bool Game::isNewBlock()
{
    QPoint head = snake->getHead();
    if(head.x()== block->x() && head.y() == block->y())
    {
        return true;
    }
    return false;
}

void Game::AddNewBlock()
{
    snake->AddBlock();
    AddPoints(eatPoints);
    NewBlock();
    emit onSnakeBlockCountChanged(snake->getCountBlocks());
}

void Game::MoveByTime()
{
    Move();
}

bool Game::Move()
{
    if (pressedDirections->count() > 0)
    {
        snake->ChangeDirection(pressedDirections->dequeue());
    }

    ClearBlock(snake->getTail());
    if(!snake->Move())
    {
        End();
        return false;
    }

    CheckSnakeOutOfMap();

    if(isNewBlock())
    {
        AddNewBlock();
    }

    DrawSnakeBlock(snake->getHead());
    return true;
}

void Game::NewBlock()
{
    bool contains = false;
    qsrand(QDateTime::currentMSecsSinceEpoch());
    //checking that place new block not on snake
    do
    {
        contains = false;
        block = new QPoint(qrand() % width, qrand() % height);
        if (snake->getBlockPositions()->contains(*block))
        {
            contains = true;
            delete block;
        }
    }
    while(contains);
    DrawBlock();
}

void Game::Draw(const QPoint& point, const QBrush& color)
{
    int x = point.x() * blockDraw->getSize();//+1;
    int y = point.y() * blockDraw->getSize();//+1;
    QPoint p(x,y);
    blockDraw->DrawBlock(p, color);
}

void Game::ClearBlock(const QPoint& p)
{
    Draw(p, *backgroundColor);
}

void Game::DrawSnakeBlock(const QPoint& p)
{
    Draw(p, *snakeColor);
}

void Game::DrawBlock()
{
    Draw(*block, *blockColor);
}

void Game::AddSnakeBlockFinish(const QPoint& block)
{
    DrawSnakeBlock(snake->getTail());
}

void Game::DrawSnake()
{
    const QPolygon* snakeBlocks = snake->getBlockPositions();
    for(int i=0; i<snake->getCountBlocks(); i++)
    {
        DrawSnakeBlock(snakeBlocks->point(i));
    }
}

void Game::DrawBackground()
{
    QPoint point;
    for (int i = 0; i < width+1; i++)
    {
        for (int j = 0; j < height+1; j++)
        {
            point.rx()=i;
            point.ry()=j;
            Draw(point, *backgroundColor);
        }
    }
}

void Game::setPoints(int points)
{
    this->points = points;
    emit onPointsChanged(points);
}

void Game::AddPoints(int points)
{
    this->points+=points;
    emit onPointsChanged(this->points);
}

void Game::setSpeed(Game::Speed sp)
{
    speed = sp;
    moveTimer->setInterval(speed);
}

Game::Speed Game::getSpeed()
{
    return speed;
}

void Game::KeyPressHandle(QKeyEvent* e)
{
    switch (e->key())
    {
    case Qt::Key_Up:
    case Qt::Key_W:
        pressedDirections->enqueue(Snake::Top);
        break;
    case Qt::Key_Left:
    case Qt::Key_A:
        pressedDirections->enqueue(Snake::Left);
        break;
    case Qt::Key_Down:
    case Qt::Key_S:
        pressedDirections->enqueue(Snake::Bottom);
        break;
    case Qt::Key_Right:
    case Qt::Key_D:
        pressedDirections->enqueue(Snake::Right);
        break;
    }
}

void Game::CheckSnakeOutOfMap()
{
    QPoint head = snake->getHead();
    if(head.x() > width)
    {
        QPoint* point = new QPoint(0,head.y());
        snake->setHead(*point);
    }
    if(head.x() < 0)
    {
        QPoint* point = new QPoint(width,head.y());
        snake->setHead(*point);
    }
    if(head.y() > height)
    {
        QPoint* point = new QPoint(head.x(),0);
        snake->setHead(*point);
    }
    if(head.y() < 0)
    {
        QPoint* point = new QPoint(head.x(),height);
        snake->setHead(*point);
    }
}

Statistic * Game::getStatistic()
{
    return &statistic;
}

void Game::setSnakeColor(const QBrush& color)
{
    *snakeColor = color;
}

void Game::setBlockColor(const QBrush& color)
{
    *blockColor = color;
}

void Game::setBackgroundColor(const QBrush& color)
{
    *backgroundColor = color;
}

void Game::SaveResult(QString &name)
{
    statistic.AddRecord(name, points, QDate::currentDate());
}


Название: Re: Контейнеры и куда девается память
Отправлено: Igors от Август 12, 2012, 10:49
Не, уже не покажу... Поправил оказывается давно. А добавлял типа так вроде,
Цитировать
QPoint* p = new Point(x,y);
varPolygon.append(*p);
Так просто утечка, 2 копии данных, одна создана new, др. скопирована в контейнер. Первая не удаляется.

П.С. Еще вопрос напоследок, если в классе обьявить поле SomeClass c; Память выделиться под эту переменную сразу через конструктор по умолчанию или нет?
Память для объекта и всех его членов (и членов членов) выделяется сразу и еще до того как управление передается конструктору. Размер этой памяти = sizeof(A).

Если не лень и будет время, просмотрите код который сделал. Игра змейка, интересны моменты по утечкам, ну и замечания по другому быдлокоду.
Посмотрю когда буду посвободнее


Название: Re: Контейнеры и куда девается память
Отправлено: Lagovas от Август 12, 2012, 11:07
Цитировать
Память для объекта и всех его членов (и членов членов) выделяется сразу и еще до того как управление передается конструктору. Размер этой памяти = sizeof(A).
А как тогда создать так обьект через конструктор с параметрами? Если писать
Код:
class A{
B b(args)
}
то жалуется.
И еще маленький такой вопрос, когда требуется ссылка в аргументе, как правильно сделать вроде такого:
Код:
void foo(QString&);
void main()
{
    foo(QString(""));
}
Жалуется что получается QString вместо Qstring&. Если писать &QString(""), жалуется что получил указатель вместо ссылки. В итоге приходится создавать переменную, задавать значение и ее уже передавать. Конечно же лучше переменная, ибо ее имя может передавать смысл и самодокументирование кодом получается, но иногда нужно задать в параметр значения по умолчанию и удобно просто передать созданный обьект через пустой конструктор.
Заранее благодарен.


Название: Re: Контейнеры и куда девается память
Отправлено: Igors от Август 12, 2012, 12:19
А как тогда создать так обьект через конструктор с параметрами? Если писать
Код:
class A{
B b(args)
}
то жалуется.
Примеры
Код
C++ (Qt)
class MyClass {
public:
MyClass( const QString & txt = "" ) : mName(txt) {}
 
QString mName;
};
 
class MyClass2 {
public:
MyClass( const QString & txt = "" ) : mStruct(str) {}
 
MyStruct mStruct;
};
 

И еще маленький такой вопрос, когда требуется ссылка в аргументе, как правильно сделать вроде такого:
Код:
void foo(QString&);
void main()
{
    foo(QString(""));
}
Жалуется что получается QString вместо Qstring&. Если писать &QString(""), жалуется что получил указатель вместо ссылки. В итоге приходится создавать переменную, задавать значение и ее уже передавать. Конечно же лучше переменная, ибо ее имя может передавать смысл и самодокументирование кодом получается, но иногда нужно задать в параметр значения по умолчанию и удобно просто передать созданный обьект через пустой конструктор.
Заранее благодарен.
Это уже проходили выше, нужна константная ссылка
Код
C++ (Qt)
void foo(const QString & str = "");
}


Название: Re: Контейнеры и куда девается память
Отправлено: Lagovas от Август 12, 2012, 12:32
Код:
class MyClass {
public:
 MyClass( const QString & txt = "" ) : mName(txt) {}
 
 QString mName;
};
 
class MyClass2 {
public:
 MyClass( const QString & txt = "" ) : mStruct(str) {}
 
 MyStruct mStruct;
};
То ли я дурак, то ли Вы опечатались где то. Во втором классе должно быть по ходу
Код:
class MyClass2{
public:
    MyClass(const QString& text = ""):mClass(str){}
MyClass mClass;
}
Это насколько я понял. Значит если поле обьект и его нужно создавать не через стандартный конструктор, то нужно инициализировать через аргументы конструктора, и никак иначе? Т.е. если в конструктор обьекта не передается параметр которым должен инициализироваться обьект-член, то нужно тогда его указателем делать и в конструкторе самому создавать? Ну и следить за удалением конечно же через деструктор. Пример например
Код:
class MyClass
{
 public:
    MyClass(int score): score(score){}
    MyClass():score(0){}
 int score;
}
class MyClass2
{
 public MyClass2(QString& s = ""):myStr(s){}
QString myStr;
MyClass mClass;
}
В этом случае получается нельзя обьявить обьект класса MyClass полем обьекта класса MyClass2 так, что бы он создавался через конструктор принимающий параметр int, если его не принимать как параметр конструктора класса MyClass2 и не инициализировать типа :mClass(intValue)?
Заранее благодарен.
П.С. Спасибо большое, уже стало яснее с параметрами.


Название: Re: Контейнеры и куда девается память
Отправлено: Igors от Август 12, 2012, 13:21
То ли я дурак, то ли Вы опечатались где то. Во втором классе должно быть по ходу
Код:
class MyClass2{
public:
    MyClass(const QString& text = ""):mClass(str){}
MyClass mClass;
}
Не вижу опечатки, Вы просто поменяли имя члена

Значит если поле обьект и его нужно создавать не через стандартный конструктор, то нужно инициализировать через аргументы конструктора, и никак иначе?
Если Вы не указываете никаких конструкторов, то будет создан по умолчанию. Однако если Вы написали конструктор(ы) с параметром, то конструктор без параметров не создается. Такой объект нельзя будет поместить в контейнер. Такой объект может быть членом до класса, но тот в своем конструкторе обязан будет вызвать конструктор этого члена с параметром. Смысл простой - невозможно создать объект минуя конструктор (ну почти), поэтому если конструктор с параметрами - он вызывается при создании.

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


Название: Re: Контейнеры и куда девается память
Отправлено: Lagovas от Август 12, 2012, 13:43
Что будет в этом случае?
Код:
class Foo
{
public:
    Foo(const QString&s):str(s){}
    QString str;
};

class Foo2
{
public:
    Foo2(){
        foo = Foo(QString("foo"));
    }
    Foo foo;
};
Что произойдет в этом коде? Тут выдает ошибку что не находит конструктор без параметров. Что бы исправить эту ошибку, я так понял можно максимум сделать
Код:
Foo2():foo(QString("foo")){...}
верно? Т.е. если там параметром должно принимать какой нить обьект посложнее, который нужно проинициализировать, все поля задать, т.е. простым созданием на месте не обойтись, то тогда нужно обьявлять Foo*? Иначе через просто Foo мы не сможем в самом конструкторе инициализировать, а через :field() будет говнокод, ибо много инициализаций придется втулить в параметры конструктора сложного Foo?
Заранее благодарен.


Название: Re: Контейнеры и куда девается память
Отправлено: Igors от Август 12, 2012, 14:14
Правильно инициализировать член не в теле конструктора, а в списке инициализаторов (после двоеточия)
Код
C++ (Qt)
class Foo2
{
public:
   Foo2() : foo(QString("foo"))
//  Foo2() : foo("foo")                // можно и так
   {
   }
   Foo foo;
};
 



Название: 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){};
void main(){
f(new Type());
}
Как я понимаю, в этом коде происходит следующее. Создается в куче обьект класса Type (тут все понятно), возвращается указатель на этот обьект. Т.е. после вызова new Type() мы получаем временный обьект Type*. Его значение (ссылка на обьект в куче) копируется в локальную переменную t функции f (действующую в ее области видимости), после этого что происходит - этот временный указатель возвращенный через new удаляется из-за выхода из области или нет? До куда его область видимости, до конца функции или сразу как скопируется в параметр функции удаляется? По идее, как все временные обьекты, должен удалиться сразу после закрывающей скобки ")" в вызове функции, либо же до закрывающей "}"?
Из-за чего вопрос. Ведь если функция принимает const type&, то можно передать временный обьект, его ссылка скопируется (только ссылка, не сам обьект), он удалится и ссылка указывает на пустое место?

В целом в чем заключается вопрос. Какая область видимости временного обьекта передаваемого как параметр? До конца функции или до ")"?
Искал в Эккеле, страуструпе... не нашел что бы черным по белому рассказывалось, что живут вот до сюда. По логике конечно живут до конца функции, но все же из-за того что я новенький в плюсах, не уверен пока более опытные не подтвердят. Сам попробовал проверить это с помощью простого кода, все вроде сходится. Кстати вопрос по коду еще:
Код:
#include <QtCore/QCoreApplication>
#include <QDebug>
#include "windows.h"

class Record{
public:
    int x;//т.к. тест, то всеравно где
    Record()
    {
        x=3;
        qDebug("record created");
    }
    ~Record()
    {
        qDebug("record destructed");
    }
    Record(const Record& r)
    {
        qDebug() << "copy constructor";
    }
};

static Record* r;//будем хранить ссылку передаваемую в функцию

void Test(const Record& rec){
    qDebug("Test start");
    //qDebug()<<"&rec="<<&rec;
    if(&(*r) != &rec)//если новая ссылка не равна старой, выводим и присваиваем
    {
     qDebug()<<"&rec="<<&rec;
     r = const_cast<Record*>(&rec);
    }
    QString s("ldgjfdklgjdfg"), ss("fsfsdfdsfsdf");
    for (int i = 0; i < 10000000; i++)
    {
        s+=ss;//что бы было какое то время для удаления временного обьекта если должен удаляться
    }
    Sleep(1000);//с той же целью
    qDebug("Test end");
}


int main(int argc, char *argv[]){
    QCoreApplication a(argc, argv);
    for(int i=0; i<10; i++)
    {
        Test(Record());
        Sleep(1000);//время что бы могла освободиться память временных обьектов и т.п.
    }
    return a.exec();
}
В итоге постоянно выводится одна и та же ссылка. Это нормально или нет? Это так хорошо с организацией памяти что каждый раз создает обьект в том же месте, или это просто ссылка на тоже место в стеке (стек ведь не меняется в программе, а расположение параметра всегда в том же есте)?
Заранее благодарен.


Название: Re: Контейнеры и куда девается память
Отправлено: Igors от Август 14, 2012, 02:16
Т.е. после вызова new Type() мы получаем временный обьект Type*. Его значение (ссылка на обьект в куче) копируется в локальную переменную t функции f (действующую в ее области видимости), после этого что происходит - этот временный указатель возвращенный через new удаляется из-за выхода из области или нет?
Никакой ссылкой в приведенном Вами коде и не пахнет. Вообще "ссылка" - это условное понятие, "физически" никаких ссылок нет, есть структуры и их адреса  - все. Вот от этой печки и пляшите.
Код
C++ (Qt)
foo(new Record());  // foo( const Record * );
 
Создали в куче объект, new вернул указатель на него (адрес). Грубо: после выполнения new адрес сидит в регистре eax. Толкнули этот адрес на стек (push eax) и вызвали foo (call [foo]). Ф-ция отработала и вернула управление. Вероятно подровняли стек (add, sp, 4) и все, никаких удалений не производится. Если ф-ция объект не удалила, он так в куче и будет.

Теперь пресловутая ссылка
Код
C++ (Qt)
fooRef(Record());  // fooRef( const Record & );
 
На стеке отвели место для безымянного Record и вызвали его конструктор. Возможно конструктор там какие-то члены создал в куче - то его дело. Толкнули опять-таки адрес Record на стек и вызвали ф-цию. Ф-ция отработала и вернула упрвление. Вызываем деструктор Record (если есть). Выравниваем стек. Готовченко.

Что неясно?



Название: Re: Контейнеры и куда девается память
Отправлено: Lagovas от Август 14, 2012, 08:16
Извиняюсь, в первом коде должно было быть const Type&, 2 часа ночи дали о себе знать...
Код:
На стеке отвели место для безымянного Record и вызвали его конструктор. Возможно конструктор там какие-то члены создал в куче - то его дело. Толкнули опять-таки адрес Record на стек и вызвали ф-цию. Ф-ция отработала и вернула упрвление. Вызываем деструктор Record (если есть). Выравниваем стек. Готовченко.
Значит область действия этого временного обьекта до конца функции, что и хотел узнать. Спасибо.