3
votes

Comment empêcher l'ajout d'un objet dans plusieurs vecteurs?

Certains objets peuvent être dessinés et certains sont mobiles .
Tous les objets mobiles sont visibles.
Je stocke tous les objets dessinables dans un vecteur appelé drawables et les objets mobiles dans un vecteur appelé movables . J'ai aussi des vecteurs navires et balles qui contiennent respectivement des objets de type Ship et Bullet . Ship et Bullet sont tous deux Movable

Voici la structure des classes:

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

Les vecteurs sont déclarés comme suit:

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

Le fait est que chaque fois que je crée un vaisseau, je dois l'ajouter dans tous les vecteurs c'est-à-dire

XXX

J'ai créé des vecteurs drawables et movables séparés car j'ai une fonction draw () qui appelle le draw () méthode de tous les objets dans le vecteur drawables . De même, j'ai une fonction move () qui appelle la méthode move () de tous les objets du vecteur movables .

Ma question est la suivante: comment changer la structure pour éviter d'ajouter la même chose dans différents vecteurs. Je dois également supprimer des objets de tous les vecteurs une fois que leur objectif est atteint.
Par exemple, une fois que la balle touche quelqu'un ou sort de l'écran, je devrai la supprimer des vecteurs drawables , movables et bullets code> après l'avoir recherché dans les trois vecteurs.
Il semble que je n'utilise pas la bonne approche pour stocker ces objets. Veuillez suggérer une alternative.

Cela ressemble plus à une question de génie logiciel qu'à une question de codage. Veuillez migrer la question vers un autre forum si nécessaire.


7 commentaires

Pouvez-vous montrer plus de code - inclure vos déclarations?


@Galik a ajouté les déclarations de vecteurs et de classes


Recherchez le système de composants d'entité, c'est une structure standard de facto pour les jeux.


Votre question contient une contradiction. Dans le texte, vous indiquez «Tous les objets dessinables sont mobiles», mais le code indique l'inverse: Movable dérive de Drawable , ce qui signifie que tous les objets mobiles sont dessinables.


Quant à la question réelle: pourquoi avez-vous les différents vecteurs en premier lieu? Si votre raison était bonne, alors peut-être que c'est correct d'avoir les mêmes choses en eux. (Puisque vous stockez des pointeurs au lieu de copies, la surcharge n'est pas énorme.) Il existe des moyens de rendre les vecteurs plus faciles à maintenir, mais vous devez d'abord décider ce que vous voulez.


@JaMiT j'ai édité cette phrase. J'ai différents vecteurs pour pouvoir appeler la fonction move () sur les objets de Movable et draw () sur tous les objets de Drawable dans des fonctions séparées. De plus, si un objet est hors limites, je peux alors effectuer une opération appropriée sur celui-ci selon qu'il se trouve dans le vecteur puces ou navires . Merci pour votre commentaire. Ce serait cool si vous suggérez une alternative pour représenter ces choses. Ce n'est pas un très gros projet et je le fais juste pour apprendre donc je ferai les changements nécessaires pour l'améliorer. Merci encore!


Notez que nous ne savons pas comment vous gérez la durée de vie de vos objets. Une réponse valide à la question telle qu'elle est écrite peut ne pas fonctionner dans votre situation spécifique. Utilisez donc un peu de jugement lors de l'évaluation des réponses.


3 Réponses :


-1
votes

Puisque tous les Movable , Ship et Bullet sont Drawable , pourquoi ne pas en créer un seul vecteur d'éléments Drawable et ajouter toutes les différentes classes à l'intérieur? Vous n'aurez qu'un seul pointeur vers toutes les différentes instances et vous n'aurez qu'un seul vecteur de ces pointeurs. Cette solution peut cependant nécessiter de faire de Drawable une classe abstraite.


4 commentaires

Il y a peut-être des objets que je n'ai besoin que de dessiner et de ne pas bouger, d'où des vecteurs différents. De plus, la classe Drawable n'a que la méthode draw () et non la méthode move () . Merci de répondre. Veuillez préciser votre réponse ou commentaire si vous pensez que j'ai manqué quelque chose.


@MaheshBansod Une solution simple serait soit d'implémenter la méthode move () et de la laisser vide (ce que je déconseille), soit de créer un vecteur de Movable qui contiendrait tous les objets Movable , Ship et Bullet , et créer un autre vecteur pour Drawable qui ne contiendrait que des objets qui sont Drawable mais pas Movable


C'est en fait ce que j'ai fait comme vous pouvez le voir dans la question. J'ai également créé des vecteurs navires et puces afin que je puisse ajouter leur code spécifique à l'avenir. La création de vecteurs différents pour chaque type d'objets pose le même problème que celui mentionné dans la question. Je vais voir le système de composants d'entité mentionné dans un commentaire sur ma question s'il est utile. Merci pour votre réponse.


@MaheshBansod et qu'est-ce qui vous empêche d'ajouter les instances uniquement dans les vecteurs de leurs classes?



1
votes

En supposant que vous utilisez un compilateur raisonnablement moderne, c'est exactement pourquoi shared_ptr existe.

Le problème est que vous ne savez pas quel vecteur possède l'objet, donc vous ne savez pas lequel supprimer. Les prises shared_ptr sont pour vous: il gère la durée de vie de l'objet, et le supprimera une fois la dernière référence à l'objet détruite.

Pour créer un nouveau Expédier , vous pourriez faire quelque chose comme ceci:

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

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

À ce stade, ship a 4 références (une pour chaque vecteur, et la variable ship elle-même). Elle sera automatiquement supprimée une fois qu'elle aura été supprimée des trois vecteurs et que la variable locale sera hors de portée.


1 commentaires

Ou peut-être utiliser low_ptr dans les vecteurs, s'ils ne sont pas nécessaires pour maintenir l'objet en vie. Vous pouvez effectuer les suppressions des vecteurs lors de leur itération.



0
votes

Si vous souhaitez conserver un conteneur de (pointeurs vers) tous les objets d'un certain type, vous pouvez adopter une approche RAII. Demandez au constructeur de l'objet d'ajouter au conteneur et au destructeur de le supprimer. Vous voudrez également vous assurer que rien d'autre ne modifie le conteneur, il devrait donc être un membre privé (statique) de votre classe, avec une méthode publique pour fournir un accès en lecture seule.

Encore mieux, déplacez cette logique dans son propre classe, donc il peut être réutilisé. Cela permettrait également à vos conteneurs existants de rester concentrés sur ce qu'ils font actuellement. Ils auraient juste besoin d'un nouveau membre de données du type de classe d'assistance.

Pour faciliter les suppressions, j'envisagerais d'utiliser une liste au lieu d'un vecteur . De plus, il peut être intéressant d'utiliser reference_wrapper au lieu de pointeurs. Un pointeur peut avoir une valeur nulle. Bien que vous puissiez documenter que le conteneur n'aura pas de pointeurs NULL, un reference_wrapper le transmet sans documentation supplémentaire.

Pour vous aider à démarrer, voici le début d'un modèle de classe d'assistance vous pourriez utiliser.

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{};

Les noms sont souvent difficiles à trouver. J'ai nommé ce modèle en fonction de l'obtention de quelque chose à parcourir, par exemple: All::list().


0 commentaires