IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Pointeurs et héritage - descendre le niveau d'un pointeur?


Sujet :

C++

  1. #1
    Membre éprouvé

    Homme Profil pro
    Consultant ERP
    Inscrit en
    Janvier 2013
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant ERP
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 372
    Points : 1 202
    Points
    1 202
    Par défaut Pointeurs et héritage - descendre le niveau d'un pointeur?
    Bonjour,

    Un problème rapide :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Class A
    Class B : public class A
    Class C : public class B
    Je cherche à accéder, à l'aide d'un pointeur vers A d'une instance C, à des méthodes de cette instance déclarées en C.
    1ère question : Pour une instance donnée, est ce que le pointeur vers A est le même que celui vers C? (Je me doute de la réponse )
    2ème question : Est-il possible de "descendre" le niveau du pointeur de A vers C?

    J'ai essayé de déclarer la fonction virtuellement en A (sans la déclarer en B), mais la fonction réelle en C n'est pas appelée...
    Entendu parler de RAII et d'allocation dynamique de mémoire : y-a-t-il à creuser?

    Pour expliquer la raison du pointeur vers A je cherche à parcourir un plus grand nombre d'entités que les simples C, au cours d'un algorithme lourd.
    Récupérer le niveau de pointeur inférieur d'une autre façon prend beaucoup de temps.

    Merci!

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Réponse à ta première question: oui, un C est un A, et son adresse est son adresse.
    Le compilateur appliquera de lui-même les éventuelles décalages.
    Sachant que dans ton cas précis (un seul héritage, et non virtuel), la partie A de C est bien au début de l'objet.
    Si on ne parle pas d'héritage virtuel, l'espace occupé par un objet est défini comme la suite des espaces occupés par ses classes de bases immédiates, dans l'ordre d'héritage, puis ses membres.

    Ainsi, avec struct Fille: M1, M2, M3 {int i;}, la mémoire d'un objet de type fille contient successivement M1, M2, M3 et i, avec éventuellement des espaces vide entre, pour des questions d'alignement.


    Pour que le code virtuel fonctionne, il faut un pointeur ou une référence vers A initialisée avec un B, et surout pas l'adresse d'un A.

    Pourrais-tu nous montrer un peu plus de code?
    Pourrais-tu nous dire comment tu peux savoir qu'un A est un C. ou même pourquoi tu veux ne t'occuper que des C d'un groupe de A?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Membre éprouvé

    Homme Profil pro
    Consultant ERP
    Inscrit en
    Janvier 2013
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant ERP
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 372
    Points : 1 202
    Points
    1 202
    Par défaut
    Merci pour ces détails sur la gestion de la mémoire!

    Ma situation en détail est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    A class BaseGameEntity
     
    B class MovingEntity : public BaseGameEntity
     
    C class Bot : public MovingEntity
    Bot::isShooting(){return m_bIsShooting;}
    Je cherche à déterminer la cible d'un bot, pour cela je veux parcourir un conteneur d'entités incluant les autres bots, mais aussi les joueurs ennemis, les bâtiments... J'ai donc choisi d'effectuer une récursion au niveau de BaseGameEntity.

    Dans cet algorithme, un pointeur "iterator" pointe vers une instance BaseGameEntity, qui n'est pas forcément un bot.
    Je voudrais à partir de ce pointeur accéder à la méthode isShooting(); déclarée dans la classe Bot.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    		std::list<BaseGameEntity*> CloseDudes = bot->GetSensoryMem()->GetListOfCloseDudes();
    		std::list<BaseGameEntity*>::const_iterator curEnt= CloseDudes.begin();
    		while (curEnt != CloseDudes.end() // ... : initialisation de la récursion)
    		{
     
    				//...check if it is not the current target and is shooting...
    				(*curEnt) != bot->GetTargetBot() && (*curEnt)->isShooting() )
    			{
    Seulement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (*curEnt)->isShooting()
    renvoie une erreur

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    C'est bien ce que je pensais.
    Donnes-toi une liste des bots. Ca a un vrai sens pour le jeu.
    Les bots sont des éléments très différents d'un mur ou d'une caisse: ils sont certes physiques, mais ils agissent.

    Le concept que tu cherches a un nom, le downcasting.
    Tu peux avoir une fonction virtuelle asBot, définie dans la classe de base par un throw, et dans Bot pour retourner *this.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class base{
    public:
        struct im_not_a_bot{};
     
        virtual base const& asBot() const {throw im_not_a_bot();}
        virtual base& asBot() {throw im_not_a_bot();}
    };
     
    class bot: public base{
    public:
        bot const& asBot() const {return *this;}
        bot& asBot() {return *this;}
    }
    Cela dit, si tu le mets en place, c'est une usine à plantage.
    Je te conseille très très fortement à avoir une liste des bots.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Membre éprouvé

    Homme Profil pro
    Consultant ERP
    Inscrit en
    Janvier 2013
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant ERP
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 372
    Points : 1 202
    Points
    1 202
    Par défaut
    Mes tours aussi peuvent être ennemies!

    Limpide, merci!
    Mise en garde bien reçue

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Alors si tes tours sont aussi des ennemis, envisage un héritage multiple.
    Je l'appelerai BadGuy, mais pas ShootingEntity, car le joueur peut tirer mais n'est pas un ennemi.

    Ainsi, une tour est une Entity et une BadGuyEntity, et un Bot un MovingEntity et un BadGuy
    Tu vas rapidement t'apercevoir que tu as besoin des positions de chacun, et donc que tu vas peut-être avoir besoin d'un héritage virtuel (en diamant)

    Une autre solution sera d'avoir une BadGuy qui sera templaté avec le type de BadGuy (nom de code CRTP)
    Ca donnerait ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class BaseGameEntity;
    class MovingEntity : public BaseGameEntity;
    class FixedEntity: public BaseGameEntity;
    class Bot : public MovingEntity, public BadGuy<Bot>;
    class Tower: public FixedEntity, public BadGuy<Tower>;
    On appelle ce motif CRTP pour curiously recurring template pattern ou modèle curieusement récursif.
    En effet, une classe C hérite d'une template dont le paramètre est C.
    L'intérêt, c'est que BadGuy peut utiliser le type précis de la classe.

    Voici un exemple de ce que pourrait être BadGuy:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    class BasicBadGuy {
    proctected:
        BasicBadGuy() {}
    public:
        bool is_shooting() const;
        virtual void shoot();
    }
     
    template <typename CRTP>
    class BadGuy : public BasicBadGuy{
    private:
        CRTP& that;
    proctected:
        BadGuy(CRTP& that) : that(that) {}
     
    public:
        CRTP::position_type x() const {return that.x();}
        virtual void shoot();
    };
    L'avantage, c'est que tu disposes d'une classe non template (BasicBadGuy) pour remplir un conteneur avec à la fois les bots et les tours.
    De plus, tu pourras aussi définir des spécialisations de BadGuy en cas de besoin.

    C'est la voie suivie par la STL pour les streams.
    ios_base est une classe non template, dont hérite basic_ios<CharT, Traits>, dont héritent basic_ostream et basic_istream, dont ...
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Voila comment j'imagine un peu les choses:

    Pour ainsi dire, tu as besoin de classes représentant les concepts du jeu (ce qu'on appelle les classes métiers):
    Tower, Bot, Crate, Wall, Door, Player, Creature.
    Cette dernière représente une créature neutre, comme une 'tite souris.

    En réfléchissant un peu plus, il y a aussi Projectile, Weapon, Spell/Skill.
    Weapon est un objet ramassable, qui donne l'occasion à une entité de créer des Projectiles.
    Spell n'est pas ramassable, il s'acquiert autrement, et permet à une entité de créer des Projectiles, de se soigner, de se téléporter.

    éventuellement Room et Corridor (encore qu'on peut imaginer qu'un couloir soit une salle), qui permettront d'avoir des effets d'environnements, et de faire apparaître des ennemis dans les pièces que le joueur ne voit pas.

    Sémantiquement, ces classes n'ont de lien entre elles.

    Par contre, tu auras tout un tas de classes techniques, qui permettent de factoriser du code.

    Ces classes-ci doivent être très limitées en fonctionalitée:
    • CellBound: Mobile, Tower, Crate, Pickable
      Connait sa position comme une case du plateau
    • CellBorderBound: Wall, Door
      Connait sa position comme entre deux cases.
      Selon les jeux, cette classe n'a pas forcément lieu d'être. Souvent, les murs et portes sont sur une case.
    • Mobile: Player, Bot, Creature, Projectile
      Sait se déplacer.
      Possède une vitesse actuelle, maximale, une accélération maximale
    • BadGuy: Tower et Bot.
      Sait choisir une direction de tir et décider quand tirer.
      Sachant que viser, ca peut vouloir dire: toujours dans une direction fixe
    • Neutral: Creature
      Juste parce que tu pourrais vouloir ajouter d'autres choses destructible
    • IA: BadGuy, Creature
      Dispose d'une aptitude (potentielle) à apprendre, et à décider
    • Living: Player, Neutral, BadGuy, Crate
      Possède un nombre de point de vie, une résistance, une régénération.
    • Pickable: Weapon
      Chose qui se rammasse. Tu peux imaginer des potions à usage immédiat
      Il pourrait avoir une taille dans l'inventaire (éventuellement, une hauteur et une largeur)
    • Picker: Player, Bot
      Les entités pouvant ramasser des objets, et donc disposant d'un inventaire


    Partant de là, je créerais des classes qui me sont nécessaires:
    • inventaire: un conteneur limité de Pickable, il possède une taille maximale, des fonctions de recherche, de sélection par case d'inventaire.
    • spell_book: un conteneur de Spell (probablement un simple typedef std::vector<Spell> spell_book)
    • cell: un type permettant d'indexer rapidement les cases. Il y a plein d'optimisations envisageable, mais tu verras ca plus tard.


    Qu'en penses-tu?
    Mon point de vue étant que programmer, c'est d'abord penser, ensuite dire à une machine ce qu'elle doit faire, en prenant le temps de traduire mes pensées en mots simple qu'elle peut comprendre.

    PS: Je vais finir par vraiment coder un jeu, à force d'expliquer comment je l'imagine
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Ton approche, leternel créera un arbre hiérarchique de classes monstrueux très rapidement.

    Actuellement, pour ce type de jeu (FPS, RPG), on a tendance à privilégier une approche ECS plutôt qu'une architecture à base de hiérarchie de classe.

  9. #9
    Membre éprouvé

    Homme Profil pro
    Consultant ERP
    Inscrit en
    Janvier 2013
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant ERP
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 372
    Points : 1 202
    Points
    1 202
    Par défaut
    Je m'appuie sur l'excellente codebase de Matt Buckland's Programming Game AI by example.
    Tous les modules que tu as évoqués, Leternel, y sont en effet implémentés, et j'ai pris soin de développer ceux qui me manquaient!

    J'évite à tout prix de me retrouver dans un héritage multiple en diamant - diamond of dread.
    Le CRTP pourrait convenir, mais j'ai encore l'espoir d'éviter tout court les héritages multiples.
    Merci Bacelar pour m'avoir éclairé sur les ECS, ce serait définitivement l'architecture à adopter pour un projet plus complexe!
    Toutefois je travaille sur un embryon de RPG : la hiérarchie de classes me suffira amplement.

    La solution qui me convient est la suivante :
    Les obstacles sont gérés par Hierarchical Anotated A* et raycasts.
    J'intègre les entités "badguy" (bot et tour pour l'instant) à une cellspacepartition et y mène mes récursions à l'échelle BaseGameEntity pour la sélection des cibles et les collision detection raycasts.
    Les rares fonctions implémentées dans les classes filles, dont j'aurai besoin pendant les récursions, seront appelées à l'aide de downcasting.

    Mieux vaudrait à ce stade migrer ce topic vers gamedev.net
    Merci encore pour vos réponses, vu leur qualité, je posterai ici le github du projet, d'ici quelques semaines, s'il peut inspirer d'autres débutants.

  10. #10
    Membre éprouvé

    Homme Profil pro
    Consultant ERP
    Inscrit en
    Janvier 2013
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant ERP
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 372
    Points : 1 202
    Points
    1 202
    Par défaut
    En implémentant le downcasting, je me suis penché sur l'opérateur dynamic_cast qui a l'air adéquat ici.

  11. #11
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    J'avais imaginé un jeu type "tower defense".
    Je ne pense pas qu'une hiérarchie composée d'une vingtaine de classes, soit une telle abération.

    Pour un RPG, contenant plus de variabilité, je veux bien croire que ce soit différent.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 21
    Dernier message: 26/07/2011, 12h08
  2. Comment descendre le niveau d'un element treeview au niveau racine
    Par dehorter olivier dans le forum Composants VCL
    Réponses: 2
    Dernier message: 25/10/2010, 08h44
  3. Containers pointeurs et héritage
    Par philagui dans le forum C++
    Réponses: 2
    Dernier message: 01/05/2006, 15h52
  4. Pointeur et héritage...
    Par Zenol dans le forum C++
    Réponses: 2
    Dernier message: 08/03/2006, 12h31
  5. Réponses: 8
    Dernier message: 04/08/2004, 14h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo