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

Langage C++ Discussion :

petit excercice piège vector et références


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 299
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 299
    Billets dans le blog
    2
    Par défaut petit excercice piège vector et références
    Bonjour,

    je suis tombé sur ce problème récemment, alors je vous le propose sous forme d'exercice. Je vous préviens, c'est tordu (en plus j'ai rajouté du bruit).

    Q1: qu'affiche le programme suivant?
    Q2: pourquoi?

    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
    #include <iostream>
     
    #include <string>
    #include <vector>
     
    using namespace std;
     
    struct Foo
    {
    	Foo( const string & name = "n/a" ) : name(name) {}
    	string name;
    };
     
     
    int main() 
    { 
    	vector<Foo> fooes;
    	fooes.push_back( Foo( "first foo" ) );
    	Foo & first_foo = fooes.back();
    	Foo & second_foo = first_foo;
    	fooes.emplace_back( Foo( "second foo" ) );
    	second_foo = fooes.back();
     
    	cout << "foo1: " << first_foo.name << endl;
    	cout << "foo2: " << second_foo.name << endl;
     
    	getchar();
    	return 0; 
    }

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par défaut
    Ma réponse:

    Les deux affichent "second foo", parce que les deux références pointent sur le même Foo (le premier), qui a été écrasé par le second à la ligne second_foo = fooes.back();.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 299
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 299
    Billets dans le blog
    2
    Par défaut
    Et non!
    Mais ça prouve que tu as été honnête et que tu as essayé de trouver sans compiler/exécuter.
    Je ne donne pas la réponse tout de suite, peut-être que d'autres ont envie de chercher

  4. #4
    Membre Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Par défaut
    Comportement indéterminé ?
    Le emplace_back peut provoquer une réallocation.

  5. #5
    Membre Expert Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 050
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 050
    Par défaut
    Ca crash nan? emplace_back ne fait aucune copie si je crois me rappeler et la Foo exite uniquement à l'appel de emplace_back. Donc second_foo.name crashera ou vaudra tout et n'importe quoi. Ou alors emplace_back fait une copie mais je ne sais pas trop.

    EDIT : Autant pour moi c'est du move -_-'

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Quand j'étudiais, j'avais un prof qui n'hésitait jamais à "jouer au compilateur"... je vais faire pareil ici:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fooes.push_back( Foo( "first foo" ) );
    ajoutes à fooes un nouvel élément dont la chaine de caractères correspond à "first foo"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Foo & first_foo = fooes.back();
    récupère une référence (!!!) sur le dernier élément du vecteur, à savoir, la structure pour laquelle la chaine est "first foo"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Foo & second_foo = first_foo;
    crée simplement une autre variable, qui fait référence au même élément
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fooes.emplace_back( Foo( "second foo" ) );
    ajoute, en utilisant la sémantique de mouvement, un deuxième élément dont la chaine est "second foo".

    A priori, cela ne pose pas de problème en soi, sauf que... C'est pour les éléments de fooes que la sémantique de mouvement est utilisée, ce qui, à mon sens, n'empêche en rien le vecteur d'augmenter sa capacité en cas de besoin.

    Il est fort vraisemblable que les objets soient placé dans l'espace nouvellement créé par mouvement, mais, quoi qu'il en soit, je dirais que les deux références se réfèrent à des éléments invalides.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	second_foo = fooes.back();
    récupère le dernier élément de fooes (celle dont la ligne est "second foo").

    second_foo fait bel et bien référence à cet objet (qui est tout à fait valide), mais l'opérateur d'affectation n'est pas récursif / transitif.

    Pour moi, first_foo fait toujours référence à un objet invalide

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << "foo1: " << first_foo.name << endl;
    A priori, comportement indéterminé parce que first_foo fait référence à un objet invalide.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << "foo2: " << second_foo.name << endl;
    affiche "second string", quoi qu'il advienne

    La seule chose dont je ne sois pas tout à fait sur, c'est le comportement de first_foo dans l'histoire.

    J'aurais tendance à dire que, l'objet d'origine ayant de toutes manières été déplacé (que ce soit en utilisant la sémantique de mouvement ne change rien à l'histoire), ou risquant de l'avoir été (tout dépend de la capacité maximale créée par défaut pour ton implémentation spécifique), on ne devrait plus tenter le moindre accès à first_foo car on ne peut pas garantir qu'il n'aura justement pas été déplacé et que la référence soit donc toujours valide.

    Les autres types de collections n'auraient sans doute pas posé ce genre de problème car elles sont (en théorie du moins) basées sur des pointeurs permettant de mettre les différents éléments en relation les uns avec les autres, mais std::vector devant de toutes manières assurer la contiguité des données, il est toujours susceptible de devoir (au mieux) déplacer les éléments qu'il contient
    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

  7. #7
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 299
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 299
    Billets dans le blog
    2
    Par défaut
    En effet vous avez raison, bien joué!
    On a bien un comportement indéterminé sur le premier cout, mais en fait, j'ai bien testé avant de poster, et moi j'obtiens toujours un crash, même en release. Le 2eme cout c'est le même problème puisque la référence est la même.

    Ici en fait, le emplace_back ne change rien à l'affaire, c'était juste pour brouiller les pistes. On aurait pu avoir un push_back c'était pareil.
    De même que la deuxième variable qui référence la première, c'est juste pour brouiller les pistes.
    En fait, c'est l'algorithme de gestion de la mémoire de la classe vector qui crée le problème ici (allocation dynamique + contigüité des éléments), et le fait qu'on déclare une/des nouvelle(s) variable(s) entre les deux push_back, ce qui implique le déplacement en mémoire du vector entier, et donc les références ne sont plus valides.

    J'ai rencontré ça dans un code récemment, et j'ai eu un mal fou à trouver le problème, car ça peut crasher à n'importe quel moment (quand on utilise la variable), et quand c'est noyé dans du code complexe et fourni, c'est l'enfer.
    J'hésite de rajouter cet exercice dans notre test d'embauche. C'est peut-être un peu fourbe quand-même.

  8. #8
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 299
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 299
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Les autres types de collections n'auraient sans doute pas posé ce genre de problème car elles sont (en théorie du moins) basées sur des pointeurs permettant de mettre les différents éléments en relation les uns avec les autres, mais std::vector devant de toutes manières assurer la contiguité des données, il est toujours susceptible de devoir (au mieux) déplacer les éléments qu'il contient
    En effet, avec les listes et les heaps, on n'aurait pas eu le problème.
    En revanche, avec les maps et les sets, qui sont des tree (red-black tree pour être précis), la gestion de la mémoire est complexe et je serais étonné que ce genre de choses se passent bien.

  9. #9
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	second_foo = fooes.back();
    récupère le dernier élément de fooes (celle dont la ligne est "second foo").

    second_foo fait bel et bien référence à cet objet (qui est tout à fait valide), mais l'opérateur d'affectation n'est pas récursif / transitif.

    Pour moi, first_foo fait toujours référence à un objet invalide

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << "foo1: " << first_foo.name << endl;
    A priori, comportement indéterminé parce que first_foo fait référence à un objet invalide.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << "foo2: " << second_foo.name << endl;
    affiche "second string", quoi qu'il advienne
    Ces paroles impliquent que dans ton raisonnement la référence second_foo a été modifiée et les deux références ne pointent plus vers le même objet (ce qui est impossible).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

Discussions similaires

  1. Petit problème de vector
    Par Nicogo dans le forum C++
    Réponses: 14
    Dernier message: 16/05/2013, 16h59
  2. petit probleme avec Vector
    Par Msakeni dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 11/12/2008, 19h28
  3. [Debutant][Vector]Petit problème de vector
    Par GyZmoO dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 25/05/2006, 10h39
  4. Problème avec vector par référence
    Par vdumont dans le forum SL & STL
    Réponses: 11
    Dernier message: 09/05/2006, 08h25

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