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

SL & STL C++ Discussion :

fonctionnement de std::vector


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut fonctionnement de std::vector
    Bonjour à tous

    J'ai un problème pour comprendre et résoudre une erreur d'exécution lors de l'ajout d'un élément dans un vecteur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class A { /*complexe, plein de sous-classe... */ };
    std::vector<A> v;
    A a;
    v.push_back(a);
    v.back().method();
    Ce code ne produit pas d'erreur, j'ai la séquence d'appel constructeur/destructeur attendue : default construteur A (variable a) -> copy constructeur A (dans le push_back) -> destructeur A (variable a) -> appel méthode, mon vecteur contient bien 1 élément et la méthode est exécutée.

    Par contre, avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class A { /*complexe, plein de sous-classe... */ };
    std::vector<A> v;
    v.resize(v.size()+1);
    v.back().method();
    J'ai une erreur. Mon objet est détruit directement après le resize et mon vecteur est vide. J'ai la séquence suivante : default constructeur (dans le resize) -> default destructeur (dans le resize) -> appel méthode = erreur (normal puisque mon objet est détruit).

    1. Pourquoi ce comportement différent ?
    2. Comment trouver l'erreur ?
    A priori, mon constructeur par défaut est correct puisqu'il ne produit pas d'erreur dans le premier code...

    Merci pour vos conseils

    PS: en complément, pas d'exception produite.
    système Ubuntu 64b, gcc 4.3

  2. #2
    Membre averti
    Inscrit en
    Février 2008
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 20
    Par défaut
    Pourquoi tu utilises resize ? Juste pour ajouter un élément ?

    Dans ce cas je ferais plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    std::vector<A> v;
    v.push_back(A);
    v.back().method();

  3. #3
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    T'es sur que c'est pas ta classe qui est bizarre, ici :

    Citation Envoyé par gbdivers Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class A { /*complexe, plein de sous-classe... */ };
    std::vector<A> v;
    A a;
    v.push_back(a);
    v.back().method();
    constructeur/destructeur attendue : default construteur A (variable a) -> copy constructeur A (dans le push_back) -> destructeur A (variable a) -> appel méthode, mon vecteur contient bien 1 élément et la méthode est exécutée.
    Je vois pas pourquoi le destructeur de a est appelé puisque ce dernier est encore à portée.

    Si tu essaies avec un objet simple comme une std::pair<int, int>, ça donne quoi?

    Resize est censé faire autant de push_back() qu'il faut avec par défaut des A() pour atteindre la taille donnée, je vois pas pourquoi ça ne fonctionnerait pas.

  4. #4
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Peut-être que resize utilise l'opérateur d'affectation et pas le constructeur par copie ? Tu as vérifié que tes objets définissent bien l'opérateur= ?

    Edit : Après vérification, l'implémentation de resize() dans la STL livré avec vs2005 ne fait pas d'appel à l'opérateur=.

  5. #5
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Si je me suis pas gourré, dans mingw, lui même basé sur gcc, resize provoque un appel à insert :

    On constate cette ligne :
    value_type __x_copy = __x;

    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
    60
    void
        vector<_Tp,_Alloc>::
        _M_fill_insert(iterator __position, size_type __n, const value_type& __x)
        {
          if (__n != 0)
          {
            if (size_type(this->_M_impl._M_end_of_storage - this->_M_impl._M_finish) >= __n)
    	  {
               value_type __x_copy = __x;
    	   const size_type __elems_after = end() - __position;
    	   iterator __old_finish(this->_M_impl._M_finish);
    	   if (__elems_after > __n)
    	     {
    	       std::uninitialized_copy(this->_M_impl._M_finish - __n,
    				       this->_M_impl._M_finish,
    				       this->_M_impl._M_finish);
    	       this->_M_impl._M_finish += __n;
    	       std::copy_backward(__position, __old_finish - __n, __old_finish);
    	       std::fill(__position, __position + __n, __x_copy);
    	     }
    	   else
    	     {
    	       std::uninitialized_fill_n(this->_M_impl._M_finish,
    					 __n - __elems_after,
    					 __x_copy);
    	       this->_M_impl._M_finish += __n - __elems_after;
    	       std::uninitialized_copy(__position, __old_finish, this->_M_impl._M_finish);
    	       this->_M_impl._M_finish += __elems_after;
    	       std::fill(__position, __old_finish, __x_copy);
    	     }
    	  }
            else
    	  {
    	    const size_type __old_size = size();
    	    const size_type __len = __old_size + std::max(__old_size, __n);
    	    iterator __new_start(this->_M_allocate(__len));
    	    iterator __new_finish(__new_start);
    	    try
    	      {
    		__new_finish = std::uninitialized_copy(begin(), __position,
    						       __new_start);
    		__new_finish = std::uninitialized_fill_n(__new_finish, __n, __x);
    		__new_finish = std::uninitialized_copy(__position, end(),
    						       __new_finish);
    	      }
    	    catch(...)
    	      {
    		std::_Destroy(__new_start,__new_finish);
    		_M_deallocate(__new_start.base(),__len);
    		__throw_exception_again;
    	      }
    	    std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish);
    	    _M_deallocate(this->_M_impl._M_start,
    			  this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
    	    this->_M_impl._M_start = __new_start.base();
    	    this->_M_impl._M_finish = __new_finish.base();
    	    this->_M_impl._M_end_of_storage = __new_start.base() + __len;
    	  }
          }
        }

  6. #6
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Citation Envoyé par _skip Voir le message
    Si je me suis pas gourré, dans mingw, lui même basé sur gcc, resize provoque un appel à insert :

    On constate cette ligne :
    value_type __x_copy = __x;
    ça reste du constructeur par copie. ça aurait été une affectation si le code se présentait comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    value_type __x_copy;
    __x_copy = x ; // op =
    Pour l'erreur original, j'avoue que je ne vois pas du tout. Il faudrait débugger en pas à pas pour voir ce que fait réellement resize().

  7. #7
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Merci pour vos pistes.
    J'ai ajouté l'opérateur d'affectation (je ne savais pas qu'il y en avait un par défaut, je m'attendais à une erreur).

    Pour compléter :
    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
    60
     
    static int s_id = 0;
    class A 
    { 
     int id;
     
     A()
     {
      id = ++s_id;
      cout << "constructor : id " << id << endl;
      ...
     }
     
     A(const A& a)
     {
      id = ++s_id;
      cout << "copy constructor : id " << id << endl;
      ...
     }
     
     ~A()
     {
      cout << "destructor : id " << id << endl;
      ...
     }
     
     A& A::operator= (const A& a)
     {
      cout << "operator= : id " << id << endl;
      ...
     }
     
    /*complexe, plein de sous-classe... */ 
    };
     
    class B
    {
     std::vector<A> v;
     
     void method2()
     {
       cout << "1. vector size : " << v.size() << endl;
       {
         A a;
         v.push_back(a);
       }
     
       cout << endl << "2. vector size : " << v.size() << endl;
       {
         v.push_back(A());
       }
     
       cout << endl << "3. vector size : " << v.size() << endl;
       {
        v.resize(v.size()+5);
       }
     
       cout << endl << "4. vector size : " << v.size() << endl;
     }
    };
    Et les messages affichés (id est un numéro unique pour chaque objet créé) :
    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
    1. vector size : 0
    constructor : id 1
    copy constructor : id 2
    destructor : id 1
     
    2. vector size : 1
    constructor : id 3
    copy constructor : id 4
    destructor : id 3
     
    3. vector size : 2
    constructor : id 5
    copy constructor : id 6
    copy constructor : id 7
    copy constructor : id 8
    copy constructor : id 9
    copy constructor : id 10
    copy constructor : id 11
    delete : id 6
    delete : id 5
     
    Erreur de segmentation
    Donc seul le constructeur de copie est utilisé et pas l'opérateur d'affectation.
    Le programme plante dans le resize et n'arrive jamais à l'étape 4. Je ne sais pas ce qui génère l'erreur de segmentation.

    Pourquoi je n'utilise pas push_back ? en fait, c'est ce que je fais. Mais le programme d'origine que j'ai repris utilisait la méthode avec resize et je voulais comprendre le pourquoi du problème.

    Edit : je comprend pas très bien pourquoi il y a 6 appels au constructeur de copie dans l'étape 3 (et 2 appels au destructeur)...

    Edit2 : question subsidiaire : est-il possible de désactiver la création automatique de l'opérateur d'affectation par défaut ?

  8. #8
    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
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Edit : je comprend pas très bien pourquoi il y a 6 appels au constructeur de copie dans l'étape 3 (et 2 appels au destructeur)...
    Je crois que c'est ça :
    3. vector size : 2
    constructor : id 5 -> Pour le second argument de resize(size_t, A=A()); (2)
    copy constructor : id 6 -> Pour le passage à insert (1)
    copy constructor : id 7 -> pour la construction du 1 élément
    copy constructor : id 8 -> pour la construction du 2 élément
    copy constructor : id 9 -> pour la construction du 3 élément
    copy constructor : id 10 -> pour la construction du 4 élément
    copy constructor : id 11 -> pour la construction du 5 élément
    delete : id 6 -> destruction de (1)
    delete : id 5 -> destruction de (2)

    Citation Envoyé par gbdivers Voir le message
    Edit2 : question subsidiaire : est-il possible de désactiver la création automatique de l'opérateur d'affectation par défaut ?
    Oui tu le déclares privé dans A mais ne le définit pas. :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class A
    {
    private:
       A&operator=(A const &); // surtout ne pas lui donner d'implémentation
    };
    Ton problème est en revanche très étrange. Ta classe A n'aurait-elle pas un pointeur nu comme membre détruit dans le destructeur ?

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

Discussions similaires

  1. std::vector : dynamique ou statique, pile et tas
    Par salseropom dans le forum SL & STL
    Réponses: 7
    Dernier message: 24/01/2005, 13h22
  2. std::sort() sur std::vector()
    Par tut dans le forum SL & STL
    Réponses: 20
    Dernier message: 05/01/2005, 19h15
  3. char[50] et std::vector<>
    Par tut dans le forum SL & STL
    Réponses: 9
    Dernier message: 12/10/2004, 13h26
  4. Réponses: 8
    Dernier message: 26/08/2004, 18h59
  5. Sauvegarde std::vector dans un .ini
    Par mick74 dans le forum MFC
    Réponses: 2
    Dernier message: 12/05/2004, 13h30

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