Как предотвратить добавление объекта в нескольких векторах? - PullRequest
3 голосов
/ 01 мая 2019

Есть некоторые объекты, которые Drawable, а некоторые Movable.
Все подвижные объекты можно прокрутить.
Я сохраняю все нарисованные объекты в векторе с именем drawables, а подвижные объекты - ввектор называется movables.У меня также есть векторы ships и bullets, которые содержат объекты типа Ship и Bullet соответственно.Ship и Bullet оба являются Movable

Вот структура классов:

class Drawable {
public:
    void draw();
};

class Movable : public Drawable {
public:
    void move();
}

class Ship : public Movable {
public:
    Ship();
}

class Bullet : public Movable {
public:
    Bullet();
}

Векторы объявлены следующим образом:

std::vector<Drawable*> drawables;
std::vector<Movable*> movables;
std::vector<Ship*> ships;
std::vector<Bullet*> bullets;

Дело в том, что каждый раз, когда я создаю Корабль, я должен добавлять его во все векторы, т.е.

drawables.push_back(ship);
movables.push_back(ship);
ships.push_back(ship);

Я создал отдельные векторы drawables и movables, так как у меня есть функция draw()который вызывает метод draw() всех объектов в векторе drawables.Точно так же у меня есть функция move(), которая вызывает метод move() всех объектов в векторе movables.

Мой вопрос: как мне изменить структуру, чтобы предотвратить добавление одной и той же вещи в разные векторы?Мне также нужно удалить объекты из всех векторов, как только цель будет достигнута.
Например, как только пуля попадет в кого-то или выйдет за пределы экрана, мне придется удалить его из векторов drawables, movables и bullets после поиска во всех трех векторах.
Кажется, что я не использую правильный подход для хранения этих объектов.Пожалуйста, предложите альтернативу.

Это больше похоже на вопрос разработки программного обеспечения, чем на вопрос кодирования.При необходимости перенесите вопрос на другой форум.

Ответы [ 3 ]

1 голос
/ 03 мая 2019

Предполагая, что вы используете достаточно современный компилятор, именно поэтому существует shared_ptr.

Проблема в том, что вы понятия не имеете, какой вектор владеет объектом, поэтому вы нене знаю, какой из них удалить.shared_ptr берет это для вас: он управляет временем жизни объекта и удалит его после уничтожения последней ссылки на объект.

Чтобы создать новый Ship, вы могли бы сделать что-тонапример:

auto ship = std::make_shared<Ship>();

drawables.push_back(ship);
movables.push_back(ship);
ships.push_back(ship);

На данный момент ship имеет 4 ссылки (по одной для каждого вектора и самой переменной ship).Он будет автоматически удален, как только он будет удален из всех трех векторов, и локальная переменная выйдет из области видимости.

0 голосов
/ 07 мая 2019

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

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

Чтобы облегчить удаление, я бы рассмотрел использование list вместо vector. Также, возможно, стоит использовать reference_wrapper вместо указателей. Указатель может иметь нулевое значение. Хотя вы можете задокументировать, что у контейнера не будет нулевых указателей, reference_wrapper передает это без дополнительной документации.

Для начала, вот начало шаблона вспомогательного класса, который вы можете использовать.

template <class T>
class All {
        using ListType = std::list< std::reference_wrapper<T> >;

    private:
        static ListType the_list;
        // A list's iterators are rarely invalidated. For a vector, you would
        // not store an iterator but instead search when removing from the_list.
        typename ListType::iterator list_it;

    public:

        // Read-only access to the list.
        static const ListType & list() { return the_list; }

        // Construction
        ListAll() : list_it(the_list.end()) {}  // If this constructor is needed
        explicit ListAll(T & data) : list_it(the_list.insert(the_list.end(), data)) {}

        // Destruction
        ~ListAll() { if ( list_it != the_list.end() ) the_list.erase(list_it); }

        // Rule of 5
        // You should also define or delete the copy constructor, move constructor,
        // copy assignment, and move assignment.

        // If you need the default constructor, then you probably want a method like:
        //void set(T & data);
};
template <class T>
typename All<T>::ListType All<T>::the_list{};

Имена часто трудно найти. Я назвал этот шаблон, основываясь на получении чего-то для перебора, например: All<Movable>::list().

0 голосов
/ 01 мая 2019

Поскольку все Movable, Ship и Bullet равны Drawable, почему бы вам просто не сделать один вектор из Drawable элементов и добавить все различные классы внутри? У вас будет только один указатель на все разные экземпляры, и у вас будет только один вектор этих указателей. Однако для этого решения может потребоваться сделать Drawable абстрактный класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...