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 :

différence entre itérateurs et operator[]


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2012
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2012
    Messages : 10
    Points : 31
    Points
    31
    Par défaut différence entre itérateurs et operator[]
    Bonjour,

    Pour mon projet, j'ai besoin de recuperer un "block" a une position donnée et vérifier son Type.

    J'ai donc fais ce code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    if (pos < this->map.size())                                                                         
        {                                                                                                 
          Block *tmp = map[pos];                                                                          
          if (tmp->getTypeBlock() != EMPTY && tmp->getTypeBlock() != EXPLOSION)                           
            return false;                                                                                 
        }                                                                                                 
      return true;
    Normalement mon programme devrait rentrer dans le "if " mais il ne le fait pas.

    J'ai testé ce code :

    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
     
    std::vector<Block *>::iterator it = this->map.begin(); 
     
     while (it != this->map.end())                                                                       
        {                                                                                                 
          type = (*it)->getTypeBlock();                                                                                                                      
          if (type != EMPTY && type != EXPLOSION)                                                         
            {                                                                                             
              block_x = (*it)->getX();                                                                    
              block_y = (*it)->getY();                                                                    
              if (block_y == y && block_x == x)                                                           
                break;                                                                                    
            }                                                                                             
          ++it;                                                                                           
        }                                                                                           
      return (true);
    Mon programme passe dans le if.

    J'ai donc print les positions en x et en y de mon "block" pour les deux versions du code, se sont les mêmes.

    J'ai donc pour deux versions le même block mais avec un "Type" différent en fonction du type d'approche que j'ai (itérateurs et []).

    Je ne vois pas d'ou ça peut venir... Une idée ?

    Merci d'avance.

  2. #2
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Bonjour,

    Il nous est assez difficile de vous aider sans un code minimal reproduisant les effets que vous observez.

    Dans le premier cas, vous accédez à l'élément grâce à une seule dimension (pos) et dans le second grâce à deux dimensions (x et y) et nous ne savons même pas comment vous passez d'une coordonnée en deux dimensions (x, y) à une coordonnée à une dimension (pos).

  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,

    De manière générale, un itérateur est un objet qui permet de parcourir l'ensemble de la collection (comme par hasard, le terme vient du verbe "itérer" )
    De son coté, l'opérateur [] permet d'accéder directement à la donnée dont l'index est indiqué.

    Les itérateurs sont souvent associés aux boucles qui doivent s'effectuer dans l'interval représenté par l'itérateur renvoyé par la fonction begin() (qui renvoie un itérateur sur le premier élément de la collection) et celui renvoyé par la fonction end() (qui renvoie un itérateur sur... ce qui suit le dernier élément) ainsi qu'aux fonction find (et autres fonctions similaires) qui renverront l'itérateur sur l'objet qui correspond (s'il existe) ou sur l'itérateur qui serait renvoyé par la fonction end() pour indiquer que l'objet n'a pas été trouvé.

    L'énorme avantage, c'est que nous sommes donc certains de travailler dans un intervalle précis qui est de l'ordre de [begin(), end()[

    C'est d'autant plus vrai que l'opérateur [] de std::map a un comportement spécifique dans le sens où il ajoutera automatiquement un élément dont la clé correspond à celle que l'on a donné dans l'opérateur si la clé n'existe pas encore.

    Et c'est un comportement normal : l'opérateur [] renvoie une référence (éventuellement constante) sur l'objet qui correspond à la clé indiquée.

    Pour que cet opérateur puisse marcher, il faut... qu'il existe bel et bien un élément correspondant à la clé indiquée

    Si la clé n'existe pas, il faudra donc rajouter une pair clé / valeur dont la clé correspondra à celle que l'on a utilisée et dont la valeur sera le résultat... du constructeur par défaut de l'objet en question.

    Mais je crois que tu n'as peut etre pas parfaitement compris le principe du fonctionnement de la std::map (ou, du moins, le code que tu présentes peut le laisser penser).

    Il faut donc savoir que std::map est ce que l'on appelle un "tableau associatif".

    C'est à dire qu'il s'agit d'une collection dans laquelle chaque élément sera associé à une clés qui lui est propre.

    Il y a donc une très forte corrélation entre la clé (AKA: ce que tu pourrais placer entre les []) et l'élément correspondant.

    Les raisons qui poussent à utiliser une std::map sont donc celles qui feront que tu voudrais pouvoir accéder à un élément particulier sur base d'une valeur qui n'est pas forcément fournie (ni même accessible) par l'élément en question.

    Ainsi, tu pourrais très bien avoir une std::map<int, int> qui serait remplie sous une forme proche de
    std::map<int, int> lamap
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for(int i = 1; i < 11; ++i){
        lamap[i*2]=i;
    }
    dont chaque élément (les valeur de 1 à 10 inclus) serait représenté par une clé dont la valeur est égale au double de celle de l'élément.

    Par conséquent, si tu invoques la fonction size de "lamap", tu obtiendras une valeur égale à 10 (car il y aura bel et bien 10 paires "clé / valeur" dans la collection) mais les seules valeurs que tu pourrait placer entre les crochets pour obtenir un élément existant seraient 2, 4, 6, 8, 10, 12, 14, 16, 18 et 20 car il n'y a, jusqu'à présent du moins, aucune clé dont la valeur serait impaire.

    Aussi, lorsque je vois le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if (pos<this->map.size())
    {
        Block *tmp = map[pos];     
        if (tmp->getTypeBlock() != EMPTY && tmp->getTypeBlock() != EXPLOSION)
        return false;
    }
    ... j'ai franchement peur

    Peut etre as tu déjà quel sera le problème

    Bah, au cas où tu ne l'aurais pas compris, cela ne fait rien, car je vais te l'expliquer...

    Je te rappelle que, dans la situation dans laquelle j'ai laissé lamap, lamap.size() est égal à... 10!!!
    Oui, mais, du coups, si j'écris le code
    je n'autorise que les valeurs 0,1, 2, 3, 4, 5, 6, 7, 8, et 9 (et toutes les valeurs négatives, mais l'explication est déjà assez complexe sans les prendre en compte), et sur ces dix valeurs, seules 4 (2, 4, 6 et 8) correspondent à une clé qui existe déjà.

    Du coup, la suite du code va avoir deux effets pervers:
    • Non seulement, tu ne vas tester que la moitié des éléments qui se trouvent dans lamap,
    • Mais, en plus, le fonctionnement de l'opérateur[] de std::map aidant, tu vas rajouter les clés 1, 3, 5, 7 et 9
    Et ce deuxième comportement sera particulièrement désagréable car, dans le "meilleur des cas" il créera un objet par défaut (un int ayant pour valeur 0 ou un pointeur ayant pour valeur NULL (nullptr en C++11) ), dans le pire des cas, il créera un objet dont la valeur correspond... aux "crasses" laissées en mémoire par une utilisation précédente

    Dans mon code d'exemple, et avec un peu de chance, cela ne devrait pas poser énormément de problème car, en écrivant quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if(pos <lamap.size()) // mettons que pos ==3
    {
        int temp = lamap[pos]; // "avec un peu de chance" temp == 0
    }
    cela ne devrait pas poser *énormément* de problème

    Mais, avec ton code... ouchh!!! Bobo !!!

    Voyons pourquoi!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if (pos<this->map.size()) // mettons : pos < map.size() MAIS pos correspond à une clé inexistante
    {
        Block *tmp = map[pos]; // CRACK!!!"dans le meilleur des cas" tmp == NULL
        if (tmp->getTypeBlock() != EMPTY && tmp->getTypeBlock() != EXPLOSION)  //BOUMM!! déréférencement de NULL
        return false;
    }
    Dans "le meilleur des cas", si pos est bel et bien plus petit que map.size() mais que pos ne correspond à aucune clé, ton pointeur temp correspond à NULL (nullptr en C++11).

    Essayer d'accéder à une fonction membre au départ de NULL correspond à un comportement indéfini qui a de fortes chances de se traduire par... une erreur de segmentation (un crash de l'application, purement et simplement).

    Dans le pire des cas, tu obtiendras quelque chose qui "peut passer pour une adresse valide". Après tout, un pointeur n'est jamais qu'une valeur numérique entière (généralement non signée) qui correspond à l'adresse mémoire à laquelle on trouvera l'élément du type indiqué.

    Et là, ben, il peut arriver du grand tout et n'importe quoi

    Tu pourrais, tout aussi bien voir une explosion au milieu de ton écran, alors qu'il n'en faut pas (pas cool ) que de ne rien avoir (plus cool) mais, étant donné que tu n'auras pas forcément parcouru l'ensemble de ta map, tu pourrais aussi ne pas voir une explosion qui aurait du se produire (pas cool non plus)

    Maintenant, si tu es sur (mais vraiment sur et certain!!!) qu'il existe bel et bien un pointeur valide pour toute clé correspondant à pos tel que pos < map.size(), que tu n'oublieras aucune clé dans ces circonstances, et qu'il n'y a aucun "trou" entre tes clés (comprend : que toutes les clés comprises entre 0 et map.size() existent bien), cela pourrait encore passer, mais... Es-tu en mesure de le garantir
    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
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    @Koala01 :
    map est un std::vector dans son code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<Block *>::iterator it = this->map.begin();

  5. #5
    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
    Citation Envoyé par Neckara Voir le message
    @Koala01 :
    map est un std::vector dans son code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<Block *>::iterator it = this->map.begin();
    Ouuupss... je me suis laissé eu par le nom... au temps pour moi
    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

  6. #6
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut
    Pourquoi j'ai l'impression que c'est le code d'un bomberman que voilà ?

    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
    std::vector<Block *>::iterator it = this->map.begin(); 
     
     while (it != this->map.end())                                                                       
        {                                                                                                 
          type = (*it)->getTypeBlock();                                                                                                                      
          if (type != EMPTY && type != EXPLOSION)                                                         
            {                                                                                             
              block_x = (*it)->getX();                                                                    
              block_y = (*it)->getY();                                                                    
              if (block_y == y && block_x == x)                                                           
                break;                                                                                    
            }                                                                                             
          ++it;                                                                                           
        }                                                                                           
      return (true);
    Pourquoi ne pas remplacer le break par un return true ?

    Sinon je te recommande d'utiliser les itérateurs dans ce genre de cas plutôt que d'aller lire les cases une par une. Heureusement le post de koala01 explique cela très bien.

  7. #7
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2012
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2012
    Messages : 10
    Points : 31
    Points
    31
    Par défaut
    @koala01 : Oui le nom laissait à penser que c'était une map.. Merci quand même pour tes explications bien détaillées .

    Pourquoi ne pas remplacer le break par un return true ?
    En effet, dans la version "original" j'ai mis un return true, mais la j'avais mis l'exemple juste pour illustrer mes propos.. Donc le break est une erreur de ma part.

    Après quelques heures de debug, j'ai trouvé d'ou venait le problème. Il venait de bien plus haut dans le code, je n'appelais pas la bonne méthode.

    Merci de votre aide !

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

Discussions similaires

  1. Différence entre un "bidouilleur" et un Pro ?
    Par christ_mallet dans le forum Débats sur le développement - Le Best Of
    Réponses: 290
    Dernier message: 28/11/2011, 10h53
  2. différence entre itérateur et indice ( [] )
    Par ooxoo dans le forum SL & STL
    Réponses: 29
    Dernier message: 21/09/2008, 18h52
  3. Différence entre TCP, UDP, ICMP
    Par GliGli dans le forum Développement
    Réponses: 1
    Dernier message: 13/09/2002, 08h25
  4. Différences entre jmp, jz, jnz, etc
    Par christbilale dans le forum Assembleur
    Réponses: 3
    Dernier message: 05/07/2002, 15h09
  5. Réponses: 3
    Dernier message: 07/05/2002, 16h06

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