Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 20.03.2018, 23:44   #1
Ghaydn
 
Регистрация: 20.03.2018
Сообщений: 7
По умолчанию Массив экземпляров класса - как правильно создавать?

Всем привет.
После стольких лет добрался-таки до Плюсов. Раньше меня отпугивал сишный синтаксис, поэтому если что и делал, то на Фрипаскале. Ну, как делал... Раз в год на пару недель нападала хандра, начинал писать простенькие игрушки, коими развлекал домашних. По сути всё моё увлечение программированием сводится к тренировке мозгов, чтобы не застывали. Так я вообще не программист, а музыкант по профессии.

В общем, вступление закончено, теперь суть.
Недавно на фрипаскалевском форуме мне эту проблему помогли решить, но вот как оно правильно делается в С++ - не понимаю.
Есть класс. Есть массив экземпляров класса.
Есть другой класс и массив его экземпляров.
Когда что-то делаю только с первым массивом, всё вроде бы хорошо (хотя я подозреваю, что на самом деле нет). Но вот в момент появления второго массива программа начинает глючить или вообще падает, в зависимости от количества элементов. Я так понял, что экземпляры класса внутри массива надо как-то инициализировать...
В общем, вот код. При запуске сначала всё хорошо, но как только один из "кораблей" пытается "выстрелить"(в случайный момент), вот тут-то и начинается безумие. Что я делаю не так? Как сделать правильно?
Код:
#include <SFML/Graphics.hpp>
#include <math.h>

class TPoint
{
public:
    double  X, Y;
//функции------------------------
    void AddVector (double BaseX, double BaseY, double Distance, double Delta);
    void AddVector (double Distance, double Delta);
};

class SWObject
{
public:
    TPoint   Position;
    double   Speed, Angle;
//функции------------------------
    void Move();
    void Move(float Distance, float Delta);
};

class Blast : public SWObject
{
public:
    double   Power;
    int      Master; //выстреливший корабль
    bool     Empire;
};

class Ship : public SWObject
{
public:
    double   WeaponAngle,
             Acceleration,
             Health,
             BlasterPower;
    int   BlasterCharge;
    bool     Empire;
//функции------------------------
    void     Shoot(int WhoIsShooting);
};


const double pi              = 3.141592653589793238462643383279502884L;//3.14159265358979323846;
const double BlasterSpeed    = 3;
const double ShipSize        = 3;
const double MaxSpeed        = 2;
const double BaseSpeed       = 1;
const int BlasterUncharged   = 100;

int FieldWidth = 800;
int FieldHeight = 600;
Blast Blasts[100];
Ship  Ships[100];
int NumShips;
int NumBlasts;

sf::RenderWindow app(sf::VideoMode(800, 600), "Star Wars");

//--------------------------------------------------------------------------------------
void TPoint::AddVector (double BaseX, double BaseY, double Distance, double Delta)
{
X = BaseX + Distance * sin(Delta);
Y = BaseY + Distance * cos(Delta);
}

void TPoint::AddVector (double Distance, double Delta)
{
X += Distance * cos(Delta);
Y += Distance * sin(Delta);
}

double RND(double Value)
{
double TMP;

TMP = Value * (double)rand() / (double)RAND_MAX ;
return TMP;
};

void AddShip(bool Empire, double X, double Y)
{
    if(Empire) {
    Ships[NumShips].Angle               = RND(pi/2) + pi/4;
    } else {
    Ships[NumShips].Angle               = RND(pi/2) + pi*5/4;
    }
    Ships[NumShips].Position.X          = X;
    Ships[NumShips].Position.Y          = Y;
    Ships[NumShips].Empire              = Empire;
    Ships[NumShips].Speed               = RND(BaseSpeed);
    Ships[NumShips].Acceleration        = 1;
    Ships[NumShips].BlasterCharge       = rand() % 20;
    Ships[NumShips].BlasterPower        = RND(100);
    Ships[NumShips].Health              = 100;
    Ships[NumShips].WeaponAngle         = 0.1;

NumShips++;
};

void CreateEscadre(void)
{
int TMP;
NumShips = 0;
NumBlasts = 0;
    for(TMP=0; TMP<20; TMP++) {
        AddShip(true, 100 + TMP*10, 100);  //На каждый корабль Империи -
        AddShip(false, 100 + TMP*10, 500); //по кораблю повстанцев
    }
}

void MoveShips(void)
{
int TMP;
for (TMP=0; TMP < NumShips; TMP++) {
//Ускорение (если "пилот" нажал "газ" или "тормоз")
Ships[TMP].Speed *= Ships[TMP].Acceleration;
//И смещаем согласно данным
Ships[TMP].Move();
if (Ships[TMP].BlasterCharge > 0) Ships[TMP].BlasterCharge --;
//DEBUG------------------------
if (RND(1) > 0.95) Ships[TMP].Shoot(TMP);
    }
};

//Это функция смещения объекта согласно его данным
void SWObject::Move()
{
if (Angle < 0) Angle += 2 * pi;
if (Angle > (2 * pi)) Angle -= 2 * pi;
//Прибавляем к координате скорость
Position.AddVector(Speed, Angle);
};

//А это функция смещения объекта согласно любым другим данным
void SWObject::Move(float Distance, float Delta)
{
Position.AddVector(Distance, Delta);
};


//--------------------------------------------------------------------------------------

void Ship::Shoot(int WhoIsShooting)
{
if (BlasterCharge == 0)
    {
    Blasts[NumBlasts].Position          = Position;
    Blasts[NumBlasts].Position.AddVector(ShipSize+1,Angle);
    Blasts[NumBlasts].Power             = BlasterPower;
    Blasts[NumBlasts].Empire            = Empire;
    Blasts[NumBlasts].Speed             = BlasterSpeed;
    Blasts[NumBlasts].Angle             = Angle + WeaponAngle;
    Blasts[NumBlasts].Master            = WhoIsShooting;
    BlasterCharge                       = 100;
    NumBlasts ++;
    }
};

void KillShip(int Index)
{
int TMP;
for(TMP = Index; TMP < NumShips; TMP--)
    Ships[TMP] = Ships[TMP+1];

NumShips--;
};


void KillBlast(int Index)
{
int TMP;
for(TMP = Index; TMP < NumBlasts; TMP--) {
    Blasts[TMP] = Blasts[TMP+1];
}
NumBlasts--;
};

void HurtShip(int Index, double Hurt)
{
    Ships[Index].Health -= Hurt;
    if(Ships[Index].Health < 0)
        KillShip(Index);

};

void DrawShips(void)
{
int TMP;
sf::CircleShape ThisShip;
for (TMP=0; TMP < NumShips; TMP++) {
        //имперские корабли красные, повстанческие серые
        if(Ships[TMP].Empire)
        {ThisShip.setFillColor(sf::Color(128,2,2));}
        else
        {ThisShip.setFillColor(sf::Color(128,128,128));};
    ThisShip.setRadius(ShipSize);
    ThisShip.setPosition(Ships[TMP].Position.X,Ships[TMP].Position.Y);
    ThisShip.setOutlineThickness(0);

    app.draw(ThisShip);
    }
};

void DrawBlasts(void)
{
int TMP;
for (TMP=0; TMP < NumBlasts; TMP++) {
    //Здесь будет функция отрисовки отдельного выстрела
    }
};

void MoveBlasts(void)
{
int TMP;
for (TMP=0; TMP < NumBlasts; TMP++) {
Blasts[TMP].Move(Blasts[TMP].Speed, Blasts[TMP].Angle);
    };
};

void AiShips(void)
{
int TMP;
for (TMP=0; TMP < NumShips; TMP++) {
    /*

    */
    }
};

int main(void)
{
    CreateEscadre();
    NumBlasts = 0;
	// Start the game loop
    while (app.isOpen())
    {
        // Process events
        sf::Event event;
        while (app.pollEvent(event))
        {
            // Close window : exit
            if (event.type == sf::Event::Closed)
                app.close();
        }

        // Очистка
        app.clear();

       // AiShips();
        MoveShips();
        MoveBlasts();

        //прорисовка поля боя
        DrawShips();
       // DrawBlasts();

        // Перестановка буферов
        app.display();
    }

    return EXIT_SUCCESS;
}
Конечно же, долго и упорно гуглил, ответа не нашёл.
— Доктор, меня не понимают...
— вРН БШ ЯЙЮГЮКХ?
Ghaydn вне форума Ответить с цитированием
Старый 21.03.2018, 04:44   #2
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

1. Экземляры класса (в т.ч. внутри массивов) инициализируются конструктором класса. Т.о. у вас там уже 100 объектов инициализированых конструктором по умолчанию.
2. Так вобщем нифига не эффективно делать. Т.к. эти массивы будут на стеке. А стек обычно пренебрежительно мал. Скажем, в линукс по умолчанию 2мб на поток. В мак-ос 512кб. Каждое число double требует 8 байт + выравнивание и т.д.
3. В игровом процессе было бы сделать целесообразно массив указателей (а точнее, класс-контейнер указателей, типа std::vector). Указатели ессно не обычные, а std::shared_ptr, т.к. они автоматически почистят память (типа сборщик мусора). Вот тогда да, каждый элемент такого массива нужно будет инициализировать путем выделения памяти для объекта класса, который инициализиуется своим конструктором.
4. Еще круче для игр, сделать пул объектов (указателей), например 1000 при старте. Из него брать нужное число и добавлять в текущее игровое, а потом вместо удаления - возвращать в пул. Удаление выполнять по завершении программы (или std::share_ptr позаботится сам). Это позволит избежать медленого выделения памяти.

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

Код:
for(TMP = Index; TMP < NumBlasts; TMP--) {
    Blasts[TMP] = Blasts[TMP+1];
будет лишь копирование их адресов.

Ну и потом, а не выходите ли вы за границы массива? Я там в упор проверок не вижу, что numblast не превышает размер массива.

5. Для вашей цикличной структуры хорошо бы пошло std::queue вместо массива.
https://habrahabr.ru/company/infopulse/blog/194726/

Последний раз редактировалось alexzk; 21.03.2018 в 05:03.
alexzk вне форума Ответить с цитированием
Старый 21.03.2018, 09:12   #3
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

Цитата:
Сообщение от alexzk Посмотреть сообщение
спользование указателей работает в разы быстрее, т.к. вместо полного копирования объекта
Ну во многих случаях же и не указатели можно по ссылке передавать.

Цитата:
Сообщение от alexzk Посмотреть сообщение
Еще круче для игр
взять движок типа Unity и делать игры, а не велосипеды

Вообще я не вижу особого смысла брать С++ и даже Фри Паскаль если
Цитата:
Сообщение от Ghaydn Посмотреть сообщение
программированием сводится к тренировке мозгов, чтобы не застывали
Я бы взял что-нибудь намного более высокоуровневое типа Питона или хотя бы C#/.NET, чтобы не тратить кучу времени непонятно на что вместо решения самой задачи (например даже описанная тут проблема о необъяснимом "безумии" происходит там намного реже + проще отлаживать). С++ обычно берут из-за производительности или когда нет другого выбора (уже существующий древний проект и т.п.), вряд ли у вас такие проблемы.
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.
Alex11223 вне форума Ответить с цитированием
Старый 21.03.2018, 09:26   #4
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от Alex11223 Посмотреть сообщение
С++ обычно берут из-за производительности или когда нет другого выбора (уже существующий древний проект и т.п.), вряд ли у вас такие проблемы.
Я бы поспорил...я беру или С++/Ява, или луа/баш, или помеси. Остальные не воспринимаю. Еще, иногда перл, когда нужно с минимальной головной болью сдать заказ. Обычно билд скрипт на перле, который все качает и настраивает на голой машине, чтобы собрать остальное.
alexzk вне форума Ответить с цитированием
Старый 21.03.2018, 10:15   #5
Ghaydn
 
Регистрация: 20.03.2018
Сообщений: 7
По умолчанию

Цитата:
Сообщение от alexzk Посмотреть сообщение
Экземляры класса (в т.ч. внутри массивов) инициализируются конструктором класса.
Вот оно что. А я-то думал, и почему же нагугливаемые методы не работают...
В общем, уже хорошо, что не как в Паскале. Там для классов необходимо всякий раз прописывать Foobar := TFoobar.Create, а в конце Foobar.Free и никак иначе. Что ж, тогда вполне себе жить можно, я почему-то ожидал от С++ плясок вокруг указателей.

Итог. Оказалось, что таки да, NumBlasts выходит за границы.
За советы спасибо, буду делать что-то посерьёзнее летающих кружочков - воспользуюсь.

Цитата:
Сообщение от Alex11223 Посмотреть сообщение
Еще круче для игр
взять движок типа Unity и делать игры, а не велосипеды
Отдельный лайк. Велосипеды надо делать тогда и только тогда, когда хочешь разобраться в устройстве велосипеда.
— Доктор, меня не понимают...
— вРН БШ ЯЙЮГЮКХ?
Ghaydn вне форума Ответить с цитированием
Старый 21.03.2018, 10:44   #6
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

Цитата:
Сообщение от Ghaydn Посмотреть сообщение
Там для классов необходимо всякий раз прописывать Foobar := TFoobar.Create, а в конце Foobar.Free и никак иначе
В современных версиях Фри Паскаля/Дельфи вроде бы можно сделать умные указатели как std::shared_ptr в С++ (странно что в стандартной библиотеке вроде бы нет до сих пор). Ну и TObjectList там может сам уничтожать объекты.

Цитата:
Сообщение от alexzk Посмотреть сообщение
я беру или С++/Ява
Ну Java это аналог C#/.NET, его я посоветовал потому что он приятнее/современнее Джавы, и если только для винды, то особого смысла брать Джаву нет. (впрочем вместо самой Джавы для JVM еще есть стремительно набирающий популярность язык Kotlin, мне он тоже нравится)
Цитата:
Сообщение от alexzk Посмотреть сообщение
Обычно билд скрипт на перле, который все качает и настраивает на голой машине, чтобы собрать остальное.
Сейчас для такого есть более удобные вещи типа Ansible или виртуальных машин через Vagrant или Docker.
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.
Alex11223 вне форума Ответить с цитированием
Старый 21.03.2018, 11:01   #7
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от Alex11223 Посмотреть сообщение
(впрочем вместо самой Джавы для JVM еще есть стремительно набирающий популярность язык Kotlin, мне он тоже нравится)

Сейчас для такого есть более удобные вещи типа Ansible или виртуальных машин через Vagrant или Docker.
Это вообще характерно последние лет 10. Каждый год что-то набирает популярность, Go 8 лет вот набирает, набирает...слышал - билеты все не продали на конфу в Москве...

Про виртуальные машины аналогично. На моих 8Гб рам просмотр этого форума и 1 вкладка с ютубом на паузе уже 5Гб прикушали. Поэтому даже приходится убивашки хромов и прочего прописывать в билд скрипты. Вопщем, спасибо - нет. Обойдемся без ВМ.

Цитата:
Сообщение от Ghaydn Посмотреть сообщение
всякий раз прописывать Foobar := TFoobar.Create, а в конце Foobar.Free и никак иначе. Что ж, тогда вполне себе жить можно, я почему-то ожидал от С++ плясок вокруг указателей.
В паскале 2 варианта выделения памяти в куче. Оригинальная от паскаля (через значек ^ чета там) и вот эта. С какой целью они ввели новую, я уже не помню.

Вобщем в С++ конструктор/деструктор вызывается автоматически обычно. НО! Есть возможность и ручного вызова (как в дельфи, даже сложнее в 2 операции - сначала память, а потом поверх нее конструктор), хотя это не для новичков.

Последний раз редактировалось Alex11223; 21.03.2018 в 11:10.
alexzk вне форума Ответить с цитированием
Старый 21.03.2018, 11:10   #8
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

Kotlin это не Go, а просто более приятная прагматичная замена Джаве, почти полностью совместимая с Java/JVM в обе стороны. Потому и набирает популярность (например в прошлом году стал вторым оф. языком для Андроида) в отличии от 100500 других языков JVM.

Ну вот Докер как раз по идее требует намного меньше памяти. Но сложнее разобраться с ним
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.
Alex11223 вне форума Ответить с цитированием
Старый 21.03.2018, 12:29   #9
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,695
По умолчанию

Цитата:
Сообщение от alexzk Посмотреть сообщение
На моих 8Гб рам просмотр этого форума и 1 вкладка с ютубом на паузе уже 5Гб прикушали. Поэтому даже приходится убивашки хромов и прочего прописывать в билд скрипты.
Вам бы комп проверить... У меня вкладок 30 точно открыто + 3 с ютубом и еще одна с трейлером на сайте игры и это все на на рабочей машинке с 4 Гб памяти.
p51x вне форума Ответить с цитированием
Старый 21.03.2018, 17:02   #10
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от p51x Посмотреть сообщение
Вам бы комп проверить... У меня вкладок 30 точно открыто + 3 с ютубом и еще одна с трейлером на сайте игры и это все на на рабочей машинке с 4 Гб памяти.
32 бита? там хром не обновлялся года 3.
Хотя у меня еще блокировщики рекламы. Они тож память кушают, хотя по многим списком я вынес в pivoxy. Вот у него 100мб потребление, совершенно не ровня javascript аппам в хроме.

Но в целом я согласен, индустрию пора чистить. Вот был qrcreator до в.4 на виджетах. 0.01с старт и готов к работе. Теперь мега-супер модное qml - 1с старт, 5-10 с открытие проекта...3Гб памяти уже нет. В попытках его ускорить они запилили кеширование...сломав всю идею обновления гуя без перекомпиляции...кошмар короче, как по мне творится с повсеместными интерпретаторами.

Последний раз редактировалось alexzk; 21.03.2018 в 17:30.
alexzk вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
как создавать именованные индексаторы для класса OmegaBerkut C# (си шарп) 85 15.09.2016 22:15
как правильно создавать объекты в потоке? Pcrepair Общие вопросы Delphi 2 02.04.2013 22:53
массив экземпляров класса flibustier912 Visual C++ 1 05.05.2012 01:00
2-мерный динамический массив экземпляров класса. С++ Парсифаль Общие вопросы C/C++ 1 29.12.2009 14:35
Как правильно программно создавать объекты на форме Format C: Общие вопросы Delphi 16 05.07.2007 09:08