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 :

methode const et iterateur de vecteur


Sujet :

C++

  1. #1
    Membre actif
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Points : 207
    Points
    207
    Par défaut methode const et iterateur de vecteur
    Salut,
    j'ai une question (sans doute toute bête) mais je ne comprends pas,
    voilà c'est une methode worlds::getWorld qui compile bien.
    Mais quand je veux rajouter const (pour dire qu'elle ne modifie en rien les données de la classe), alors ca ne compile plus pourtant elle ne modifie rien, elle partours juste une vecteur membre (vWorlds) avec des iterateurs.

    voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    world* worlds::getWorld( const Uint8 id ) const
    {
    	for (std::vector<world*>::iterator it=vWorlds.begin();it!=vWorlds.end();++it)
    	{
    		if ((*it)->id==id) return (*it);
    	}
    	return NULL;
    }
    et le compilo rale sur la ligne du for parce que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    error C2440: 'initializing' : cannot convert from 'class world *const * ' to 'class world ** '
            Conversion loses qualifiers
    si quelqu'un pouvais me dire ou est l'erreur bête que j'ai commis ?,
    d'avance, merci.

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    std::vector<world*>::const_iterator

  3. #3
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Pour compléter et comprendre la réponse de 3DArchi( qui est tout à fait juste ), il faut savoir qu'il existe généralement deux types d'itérateur sur les collections de la STL:
    1. Un itérateur "simple", nommé iterator, qui autorise parfaitement la modification de l'élément itéré (pour autant que cela ait un sens par rapport à la collection envisagée: il n'en a, par exemple aucun pour un std::set parce que la modification de l'élément met à mal le classement de celui-ci )
    2. Un itérateur constant, qui empêche toute modification de l'élément itéré.
    Le fait, c'est que, lorsque tu déclare une fonction constante, tu t'engage à ne pas modifier du tout l'objet au départ duquel tu va invoquer la fonction.

    Or, si on te donne l'occasion de modifier un élément de la collection qui fait partie de ton objet, on peut, par défaut (et en dehors de toute indication contraire de ta part) estimer que... tu modifiera l'objet en cours

    Et, comme cela irait en contradiction avec ton propre engagement de ne pas modifier l'objet, tu comprends que cela ne pourrait être toléré.

    C'est la raison pour laquelle tous les membres de l'objet en cours sont considérés comme... étant constant quand tu est dans le corps d'une fonction déclarée constante (sauf exception explicitement indiquée à l'aide du mot clé mutable).

    Le fait est qu'il existe deux versions pour les fonctions (ne modifiant jamais la collection) renvoyant un itérateur:
    • une version non constante, qui renvoie un itérateur "simple", et qui autorise donc de modifier l'objet itéré et
    • une version constante, qui renvoie un itérateur constat, et qui interdit donc toute modification de l'objet itéré
    Du coup, comme ta collection est considérée comme constante (vu que c'est un membre de l'objet en cours et que tu te trouve dans une fonction qui s'est explicitement engagée à ne pas modifier le modifier), c'est la verison constante de la fonction renvoyant un itérateur ... constant, qui sera appelée.

    Tu peux donc utiliser un itérateur "simple" (iterator) tant que tu as la certitude de ne pas être dans une situation dans laquelle ta collection serait considérée comme constante:
    • Si tu la passe par référence non constante à n'importe quel fonction (qu'elle soit libre ou membre d'une classe, et, dans ce cas, qu'elles soit constante ou non)
    • Quand tu travailles directement sur ta collection et que tu ne l'a pas déclarée constante
    Mais, en revanche, dés que ta collection est réputée (ou risque d'être réputée) constante, tu devra utiliser un... itérateur constant (const_iterator):
    • Quand ta collection est membre d'une classe et que tu est dans une fonction membre constante de cette classe
    • Quant ta collection est passée sous forme de référence constante à n'importe quelle fonction
    • Quand tu as explicitement déclaré la collection constante ce qui est, il faut l'avouer, relativement rare et peu intéressant
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre actif
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Points : 207
    Points
    207
    Par défaut
    Merci,
    je comprends mieux en effet.

    je crois que je m'etais déjà fais piegé il y a quelques années, j'avais oublié.

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Ceci dit, il reste un potentiellement un problème lié au retour par pointeur : rien n'empêche alors de modifier un des éléments de ton vecteur ... alors que tu as une fonction (et peut être un objet) constant. Ne serait-ce pas plutôt : world const* worlds::getWorld ?

    P.S. :
    on ne le répètera jamais assez : pointeurs intelligents !

  6. #6
    Membre actif
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Points : 207
    Points
    207
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Ceci dit, il reste un potentiellement un problème lié au retour par pointeur : rien n'empêche alors de modifier un des éléments de ton vecteur ... alors que tu as une fonction (et peut être un objet) constant. Ne serait-ce pas plutôt : world const* worlds::getWorld ?

    P.S. :
    on ne le répètera jamais assez : pointeurs intelligents !
    oui, bon là c'est applicable en effet.
    mais imaginons (c'est pas mon cas) que je souhaite modifier l'objet en question.
    vWorlds est un vecteur de pointeur vers world.
    je ne modifirai en rien le vecteur (il n'y a qu'une adresse dedans)
    même si je modifie l'objet pointé, je ne modifie en rien ma classe worlds.
    alors que l'objet world est lui en dehors de la classe worlds (je stocke juste un pointeur qui lui n'est pas modifié ni dedans getworld ni apres).

    non ?

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par hpfx Voir le message
    oui, bon là c'est applicable en effet.
    mais imaginons (c'est pas mon cas) que je souhaite modifier l'objet en question.
    vWorlds est un vecteur de pointeur vers world.
    je ne modifirai en rien le vecteur (il n'y a qu'une adresse dedans)
    même si je modifie l'objet pointé, je ne modifie en rien ma classe worlds.
    alors que l'objet world est lui en dehors de la classe worlds (je stocke juste un pointeur qui lui n'est pas modifié ni dedans getworld ni apres).
    non ?
    si en théorie retourner un pointeur const ou non const est dépendant effectivement du contexte de ta classe et de sa relation avec ses sous éléments, en pratique (à moins que tu redéfinisses un conteneur), cela m'interpellerais de retourner un pointeur non const (en fait, j'aurais déjà poussé des cris d'orfrais à retourner un pointeur (viol de l'encapsulation), et pire, un pointeur brut )

  8. #8
    Membre actif
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Points : 207
    Points
    207
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    cela m'interpellerais de retourner un pointeur non const (en fait, j'aurais déjà poussé des cris d'orfrais à retourner un pointeur (viol de l'encapsulation), et pire, un pointeur brut )
    Oui je comprends,
    en fait la classe worlds encapsule un conteneur de world, c'est à dire le vecteur.
    Alors oui, quand on vois la methode getWorld() on peu se dire que je n'avais pas besoin d'une classe, il m'aurait suffit d'utiliser un std::map.
    mais en fait je voulais cacher l'implementation du conteneur et ajouter des methodes de recherche de dependance entre world (imagine un jeu, dont le world x depends du world w etc...).
    exemple :
    parcours les dependances pour voir si les world donc dépend id sont terminés..

    Pour info,
    mon objet world etant un descripteur (nom de fichiers, liste de dependance : toutes ces choses la sont constants) ainsi que des elements dynamiques (world complete ?)
    Voilà, dans l'espris je ne verrai rien de choquant a ce que la classe worlds n'encapsule QUe le conteneur, et je pourrai l'utiliser ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    world *current = game->getWorld(id);
    current->complete(); // monde terminé : en fait il va mettre m_complete à true (donc non const!!!)
    mais pour l'instant je passe par une methode de la classe worlds :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    game->worldComplete(id)
    mais bon, c'est un peu pareil, non ?
    j'espere ne pas trop choquer, mais (je me trompe peut être hein) j'ai pas l'impression de violer l'encapsulation, car je souhaitai encapsuler un conteneur d'objet.
    Et même j'ai l'impression d'avoir fait presque trop de methodes qui n'ont rien a voir avec mon encapsulation, c'est pour celà que je me demande si je ne vais pas epurer ma classe en retirant ces petites methodes qui ne sont que des "wraper" vers les methodes de world...
    qu'en penses-tu ?

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    A priori, je dirais que l'utilisateur de worlds aurait du être générique sur le conteneur associé puisque c'est ce que tu essais d'abstraire. En d'autres termes, les templates, au vue des éléments que tu donnes, me semble un design plus pertinent pour faire varier le type du conteneur plutôt qu'une encapsulation de celui-ci dans une (incomplète et erronée ) nouvelle classe conteneur

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    J'ai l'impression que tu réfléchis un peu trop en terme de programmation structurée et non en terme de programmation orientée objets.

    Tu sembles en effet avoir suivi un raisonnement proche de:
    j'ai un certain nombre de mondes, et je veux créer une structure qui me permette de tous les avoir sous la main
    alors qu'en OO, le raisonnement aurait plutôt été proche de
    j'ai un certain nombre de mondes qui "vivent tout seuls"(on part à leur exploration et ils nous envoient différents messages lorsque l'on arrête leur exploration), et j'ai donc besoin d'une classe dont la responsabilité est de les gérer
    A ce moment là, tu te rend compte que ta classe worlds n'a, à l'extrême limite, même pas *forcément* besoin de besoin de garder en mémoire l'intégralité des mondes en un instant T:

    Tout ce qu'il lui faut, c'est la liste (ordonnée) des monde qu'elle doit manipuler, par exemple, sous la forme du nom du fichier de configuration associé à chaque monde.

    Elle pourrait alors se contenter, pour chaque fichier de configuration associé à un monde, de charger le monde correspondant et ses dépendances, de "partir à son exploration", puis de le "décharger" / détruire lorsque le monde signale qu'il a été complètement exploré avant de... charger le suivant dans la liste.

    Tu aurais donc au final une classe Wordls proche de
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    class Worlds
    {
        public: 
            /* le constructeur prend un pointeur sur le personnage qui
             * explorera le monde
             */
           Worlds(Perso *ptr);
           /* il est, éventuellement, possible de changer de personnage */
           void changePerso(Perso * newptr);
           {
               /* on détruit le perso précédant */
               delete perso_;
               /* et on utilise le nouveau */
               perso_=newptr;
           }
           /* on peut rajouter le nom d'un fichier de configuration propre à un
            * monde donné
            */
           void addWorld(std::string const & filename)
           {
               world_.push(filename);
           }
           /* on peut vouloir passer directement à un monde donné, car
            * on a déjà visité les précédents
            */
          void beginAtWorld(std::string const & filename)
          {
              while(world_.front()!=filename)
                  world_.pop();
          }
           /* et on peut demander de lancer l'exploration des mondes introduits
            */
           void run()
           {
                while(!world_.empty()
                {
                    World * current = load(world_.front());
                    current->explore(perso_);
                    unload(current);
                    world.pop();
                }
           }
        private:
            /* quelques fonctions qui ne servent qu'à usage interne : */
            World* load(std::string const & filename)
            {
                /* chargement du monde sur base du fichier de configuration */
            }
            void unload(World* w)
            {
                /* sauvegarde éventuel de l'état du monde et destruction
                 * de celui-ci
                 */
            }
            /* la référence vers le personnage courent */
            Perso * perso_;
            /* et la file des mondes à explorer */
            std::queue<std::string> world_;
    };
    C'est surement améliorable, mais le tout serait utilisé sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main()
    {
        Perso * p;
        /*sélection du personnage */
        Worlds w(p);
        /* ajout des noms de fichiers de configuration */
        w.addWorld("machinChose");
        w.addWorld("Truc");
        w.addWorld("machin");
        /* partons à l'exploration des mondes */
        w.run();
        /* nettoyage final à faire */
        return 0;
    }
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre actif
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Points : 207
    Points
    207
    Par défaut
    Citation Envoyé par koala01 Voir le message
    J'ai l'impression que tu réfléchis un peu trop en terme de programmation structurée et non en terme de programmation orientée objets.
    Oui, je penses que tu es proche de la réalité, je n'ai peut être pas le raisonenement OO qu'il faudrait.

    merci pour tes remarques,
    sinon, l'architecture est un peu pluc complexe : un world n'est qu'un ensemble de niveau (ex : 1-1, 1-2 et 1-3 sont 3 niveaux du world 1)... j'ai donc aussi une classe niveau, qui est "jouable" (avec changement de perso tout ca... mais pas de contrainte de dependance statique/declaratif) pour world je n'utilise pas la même terminologie, j'ai une methode world::openworld qui s'occupe de faire jouer le 1er niveau du world.

    1-1,1-2 et 1-3 ne sont que des exemples pour reprendre qqchose de connu (mario) mais les world ne sont pas une sequence de niveaux...

    Merci.

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

Discussions similaires

  1. iterateur sur vecteur avec objets
    Par debloc dans le forum Débuter
    Réponses: 11
    Dernier message: 04/11/2011, 19h39
  2. iterateur de vecteur 2D et erase
    Par micamused dans le forum Langage
    Réponses: 1
    Dernier message: 22/12/2010, 16h57
  3. pb avec iterateur const sur une list STL
    Par Muetdhiver dans le forum SL & STL
    Réponses: 4
    Dernier message: 14/01/2007, 16h39
  4. acces à un iterateur de vecteur par son index
    Par koala01 dans le forum C++
    Réponses: 11
    Dernier message: 25/11/2006, 10h17
  5. Réponses: 8
    Dernier message: 07/04/2006, 08h03

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