Сохранение вектора всех экземпляров классов и вызов их функций-членов - PullRequest
0 голосов
/ 07 июня 2018

Как мне создать вектор, в котором хранятся все экземпляры класса?Тогда как мне перебрать их и вызвать одну из их функций-членов?

Вот сжатый пример того, что я пытался сделать.

#include <vector>

struct Entity{

    Entity::Draw(){
        // do drawing things
    }
};

static std::vector<Entity> entities;

Entity player;
Entity enemy;

void renderEntities() {

    for (std::vector<Entity>::iterator iter = entities.begin();
            iter < entities.end();
            iter++) {

        iter->Draw; // Error in the example. I'm using Draw(); in the actual code.
}

Но renderEntities () isn 'ничего не делаю.Функция-член Draw работает, если я использую, например, player-> Draw.Я либо облажался вектором или итератором, либо и тем и другим, и я не могу понять, как это исправить.Я пытался использовать ссылки и указатели, что, я полагаю, это то, что нужно делать, но всякий раз, когда я пытаюсь это сделать, я получаю ошибки, которые я не смог исправить.

ОБНОВЛЕНИЕ: Я ценю всю помощь,Я многому учусь.Однако моя функция render_entities по-прежнему ничего не делает.Вот весь код.

Любой вызов функции, который начинается с терминала_, происходит из библиотеки BearLibTerminal.

main.cpp

#include <BLT/BearLibTerminal.h>
#include <iostream>
#include <string.h>
#include <vector>

#include "entity.h"

const int WindowSizeX{50};
const int WindowSizeY{20};
const std::string Title{"BLT Test"};
const std::string Font{"../res/SourceCodePro-Regular.ttf"};
const int FontSize{24};

bool quit_game{false};

static Entity player;
static Entity enemy;

void initialize();
void handle_input(int key, Entity &entity);
void draw_player(int x, int y, const char *symbol);
void render_entities();
void clear_entities();

int main() {

    initialize();

    while (!quit_game) {

        terminal_refresh();

        int key{terminal_read()};

        if (key != TK_CLOSE) {

            handle_input(key, player);
        }

        else {

            quit_game = true;
            break;
        }

        clear_entities();
    }

    terminal_close();

    return 0;
}

void initialize() {

    terminal_open();

    std::string size{"size=" + std::to_string(WindowSizeX) + "x" +
                     std::to_string(WindowSizeY)};
    std::string title{"title='" + Title + "'"};
    std::string window{"window: " + size + "," + title};

    std::string fontSize{"size=" + std::to_string(FontSize)};
    std::string font{"font: " + Font + ", " + fontSize};

    std::string concatWndFnt{window + "; " + font};
    const char *setWndFnt{concatWndFnt.c_str()};

    terminal_set(setWndFnt);
    terminal_clear();

    player.x = 0;
    player.y = 0;
    player.layer = 0;
    player.symbol = "P";
    player.color = "green";

    enemy.x = 10;
    enemy.y = 10;
    enemy.layer = 0;
    enemy.symbol = "N";
    enemy.color = "red";
}

void handle_input(int key, Entity &entity) {

    int dx{0};
    int dy{0};

    switch (key) {

        case TK_LEFT:
        case TK_H:
            dx = -1;
            dy = 0;
            break;

        case TK_RIGHT:
        case TK_L:
            dx = 1;
            dy = 0;
            break;

        case TK_UP:
        case TK_K:
            dx = 0;
            dy = -1;
            break;

        case TK_DOWN:
        case TK_J:
            dy = 1;
            dx = 0;
            break;

        case TK_Y:
            dx = -1;
            dy = -1;
            break;

        case TK_U:
            dx = 1;
            dy = -1;
            break;

        case TK_B:
            dx = -1;
            dy = 1;
            break;

        case TK_N:
            dx = 1;
            dy = 1;
            break;

        case TK_ESCAPE:
            quit_game = true;
            break;
    }

    player.Move(dx, dy);

    if (player.x > WindowSizeX - 1) {

        player.x = WindowSizeX - 1;
    }
    else if (player.x < 0) {

        player.x = 0;
    }

    if (player.y > WindowSizeY - 1) {

        player.y = WindowSizeY - 1;
    }
    else if (player.y < 0) {

        player.y = 0;
    }

    player.Draw(); // This works.
    enemy.Draw();  // So do this.
    entity.Draw(); // This draws only player.
    render_entities(); // This doesn't do anything.

    // Player X and Y are printed out correctly, Entities is always 0.
    std::cout << "Player X: " << player.x << std::endl;
    std::cout << "Player Y: " << player.y << std::endl;
    std::cout << "Entities: " << entities.size() << std::endl;
}

void render_entities() {

    for (auto entity : entities) {
        entity->Draw();
    }
}

void clear_entities() {

    for (auto entity : entities) {
        entity->Clear();
    }
}

entity.h

#ifndef ENTITY_H_
#define ENTITY_H_

struct Entity {

    int x;
    int y;
    int layer;
    const char *symbol;
    const char *color;

    Entity();
    ~Entity();

    void Move(int dx, int dy);
    void Draw();
    void Clear();
};

static std::vector<Entity *> entities;

#endif /* ENTITY_H_ */

entity.cpp

#include <BLT/BearLibTerminal.h>
#include <vector>
#include <algorithm>
#include "entity.h"

Entity::Entity() {

    entities.push_back(this);
}

// Entity(const Entity &) : Entity() {}
// I get an "expected unqualified-id" when I uncomment this. Why?

Entity::~Entity() {

    auto iter = std::find(entities.begin(), entities.end(), this);

    if (iter != entities.end())
        entities.erase(iter);
}

void Entity::Move(int dx, int dy) {

    this->x += dx;
    this->y += dy;
}

void Entity::Draw() {

    terminal_layer(this->layer);
    terminal_color(color_from_name(this->color));
    terminal_print(this->x, this->y, this->symbol);
}

void Entity::Clear() {

    terminal_layer(this->layer);
    terminal_print(this->x, this->y, " ");
}

В main.cpp внизу handle_input () вы увидите ...

    player.Draw(); // This works.
    enemy.Draw();  // So do this.
    entity.Draw(); // This draws only player.
    render_entities(); // This doesn't do anything.

    // Player X and Y are printed out correctly, Entities is always 0.
    std::cout << "Player X: " << player.x << std::endl;
    std::cout << "Player Y: " << player.y << std::endl;
    std::cout << "Entities: " << entities.size() << std::endl;

Ответы [ 5 ]

0 голосов
/ 07 июня 2018

Вот пример.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Entity {
    Entity(string _name):name(_name){} 
    void Draw(){
        // do drawing things
        cout << name << "::Draw" << endl;
    }
    private:
    string name;
};

static std::vector<Entity *> entities;

static Entity * player = new Entity("Player");
static Entity * enemy = new Entity("Enemy");

void renderEntities() 
{
    for( auto & entity : entities){
       entity->Draw();
    }
}

int main()
{
   entities.push_back(player);
   entities.push_back(enemy);

   renderEntities();

   return 0;
}
0 голосов
/ 07 июня 2018

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

Проблема сваш код iter->Draw; -> Это на самом деле не вызывает функцию, поэтому должно быть:

for (std::vector<Entity>::iterator iter = entities.begin();
        iter < entities.end();
        iter++) {
    iter->Draw(); // Notice I added ()
}

Однако есть лучший способ сделать то же самое:

// Entity & is important so that a copy isn't made
for (Entity & entity : entities) {
    entity.Draw();
}

Теперь эквивалентная (но немного более приятная) версия выше:

// Notice the use of auto!
for (auto & entity : entities) {
    entity.Draw();
}

Наконец, если позже вы решите, что вам нужен вектор указателей, вы можете иметь:

static std::vector<Entity *> entities;

static Entity * player = new Entity();
static Entity * enemy = new Entity();

...

for (Entity * entity : entities) { // You could also use (auto entity : entities)
    entity->Draw();
}

В этом случае, так как это вектор raw pointers, а не smart pointers, вам необходимо убедиться, что вы прошли цикл по вектору и удалили сущности в некоторой точке, прежде чем вызывать entity.clear () или вас.будет иметь утечку памяти.

Хорошая вещь в последнем примере состоит в том, что если вы позже реорганизуете свой код, чтобы иметь другие классы extend Entity, чтобы обеспечить их собственное поведение Draw, ваш вектор все равно будетиметь возможность хранить указатели на всеИспользуйте новые классы и вызовите их методы Draw ().

0 голосов
/ 07 июня 2018

Вы хотите iter != entities.end(); вместо <.Кроме того, в этом примере кода вы забыли скобки после Draw.

0 голосов
/ 07 июня 2018

renderEntities() ничего не делает, потому что вы не добавили Entity объектов в vector.Когда вы объявляете свои player и enemy объекты, они просто торчат в памяти, они автоматически не добавляются в vector.Вам нужно добавить их явно, например, вызвав entities.push_back().

. Я бы предложил использовать конструктор и деструктор Entity для автоматического обновления vector, вместо того, чтобы не делать это вручную.,Таким образом, каждый Entity объект учитывается renderEntities(), например:

#include <vector>
#include <algorithm>

struct Entity;
static std::vector<Entity*> entities;

struct Entity
{    
    Entity()
    {
        entities.push_back(this);
    }

    Entity(const Entity &)
        : Entity()
    {
    }

    ~Entity()
    {
        auto iter = std::find(entities.begin(), entities.end(), this);
        if (iter != entities.end())
            entities.erase(iter);
    }

    void Draw()
    {
        // do drawing things
    }
};

Entity player;
Entity enemy;

void renderEntities()
{
    for (auto *entity : entities)
    {
        entity->Draw();
    }
}

Live Demo


ОБНОВЛЕНИЕ : после просмотра вашего полного кода я вижу, что вы все еще допускаете ошибки.

В main.cpp нет переменной entity в области действия handle_input(), поэтому вызов entity.Draw() долженне компилируется.

Правда, большая ошибка в entity.h.НЕ объявляйте вашу entities переменную как static в этом файле!Это заставляет каждый .cpp, который #include является вашим entity.h файлом, получать свою собственную копию переменной.Это означает, что main.cpp и entity.cpp работают на отдельных std::vector объектах !Вот почему вы видите, что entities всегда пуст в main.cpp - объекты Entity никогда не добавляются к std::vector, существующему в main.cpp, только к std::vector, существующему в entities.cpp.

Необходимо переместить действительную переменную std::vector в entity.cpp (без static), а затем объявить переменную как extern в entity.h, чтобы все ваши файлы .cpp моглиполучить доступ и поделиться этой единственной переменной .

Попробуйте вместо этого:

entity.h

#ifndef ENTITY_H_
#define ENTITY_H_

#include <vector>
#include <string>

struct Entity {
    int x = 0;
    int y = 0;
    int layer = 0;
    std::string symbol;
    std::string color;

    Entity();
    Entity(const Entity&);
    ~Entity();

    Entity& operator=(const Entity&) = default;

    ...
};

extern std::vector<Entity *> entities; // <-- extern, NOT static!

#endif /* ENTITY_H_ */

entity.cpp

#include <BLT/BearLibTerminal.h>
#include <vector>
#include <algorithm>
#include "entity.h"

std::vector<Entity *> entities; // <-- real variable, also NOT static!

Entity::Entity() {
    entities.push_back(this);
}

Entity::Entity(const Entity &src) : Entity() {
    *this = src;
}

Entity::~Entity() {
    auto iter = std::find(entities.begin(), entities.end(), this);
    if (iter != entities.end())
        entities.erase(iter);
}

...

void Entity::Draw() {
    terminal_layer(layer);
    terminal_color(color_from_name(color.c_str()));
    terminal_print(x, y, symbol.c_str());
}

...
0 голосов
/ 07 июня 2018

Более простой способ с циклом for, так как c ++ 11:

for( auto & entity : entities) {
     entity.Draw();
}
...