vecteur push_back appelant copy_constructor plus d’une fois?

Je suis un peu confus avec la façon dont le vecteur push_back se comporte, avec l’extrait suivant, je m’attendais à ce que le constructeur de copie ne soit appelé que deux fois, mais la sortie suggère le contraire. Est-ce une restructuration interne de vecteur qui entraîne ce comportement?

Sortie:

Inside default Inside copy with my_int = 0 Inside copy with my_int = 0 Inside copy with my_int = 1 
 class Myint { private: int my_int; public: Myint() : my_int(0) { cout << "Inside default " << endl; } Myint(const Myint& x) : my_int(x.my_int) { cout << "Inside copy with my_int = " << x.my_int << endl; } void set(const int &x) { my_int = x; } } vector myints; Myint x; myints.push_back(x); x.set(1); myints.push_back(x); 

Ce qui se produit:

  1. x est inséré via push_back . Une copie survient: l’élément nouvellement créé est initialisé avec l’argument. my_int est repris à zéro car le constructeur par défaut de x s l’initialisait ainsi.

  2. Le deuxième élément est push_back ‘d; Le vecteur doit réaffecter la mémoire puisque la capacité interne a été atteinte.
    Comme aucun constructeur de mouvement n’est défini implicitement pour Myint 1, le constructeur de copie est choisi; Le premier élément est copié dans la mémoire nouvellement allouée (son my_int est toujours égal à zéro … donc le constructeur de la copie indique à nouveau my_int à 0 ), puis x est copié pour initialiser le deuxième élément (comme pour le premier à l’étape 1.). Cette fois, x a my_int défini sur un et c’est ce que nous indique la sortie du constructeur de la copie.

Le nombre total d’appels est donc de trois. Cela peut varier d’une mise en œuvre à une autre, car la capacité initiale peut être différente. Cependant, deux appels sont être le minimum.

Vous pouvez réduire le nombre de copies en réservant à l’avance davantage de mémoire, c’est-à-dire que la capacité des vecteurs est plus importante, de sorte que la réaffectation devient inutile:

 myints.reserve(2); // Now two elements can be inserted without reallocation. 

De plus, vous pouvez supprimer les copies lors de l’insertion comme suit:

 myints.emplace_back(0); 

Ceci “place” un nouvel élément – emplace_back est un modèle variadique et peut donc prendre une quantité arbitraire d’arguments qu’il transmet ensuite – sans copies ni déplacements – au constructeur d’éléments.

1 Parce qu’il existe un constructeur de copie déclaré par l’utilisateur.

Lorsque la taille du vecteur est augmentée avec le deuxième push_back , le contenu existant du vecteur doit être copié dans un nouveau tampon. Pour vérifier, myints.capacity() après le premier push_back , ce devrait être 1 .

Cela dépend de la quantité de mémoire réservée à un object de type std::vector . Il semble que lors de la première exécution de push_back mémoire ne soit allouée que pour un élément. Lorsque la deuxième fois push_back été appelée, la mémoire a été réaffectée pour réserver de la mémoire au deuxième élément. Dans ce cas, l’élément qui est déjà dans le vecteur est copié dans le nouvel emplacement. Et puis le deuxième élément est également ajouté.

Vous pourriez vous-même réserver assez de mémoire pour échapper au second appel du constructeur de copie:

 vector myints; myints.reserve( 2 ); 

Vous l’avez eu … c’était le redimensionnement. Mais je ferai simplement remarquer que si vous comptez quelques beans sur vos constructeurs, vous pourriez être intéressé par “l’emplacement”:

 #include  #include  using namespace std; class Myint { private: int my_int; public: explicit Myint(int value = 0) : my_int(value) { cout << "Inside default " << endl; } Myint(const Myint& x) : my_int(x.my_int) { cout << "Inside copy with my_int = " << x.my_int << endl; } Myint(const Myint&& x) noexcept : my_int(x.my_int) { cout << "Inside move with my_int = " << x.my_int << endl; } }; int main() { vector myints; myints.reserve(2); myints.emplace_back(0); myints.emplace_back(1); // your code goes here return 0; } 

Cela devrait vous donner:

 Inside default Inside default 

Et, en raison du constructeur noexcept on the move … si vous supprimez la reserve vous obtenez un déplacement, pas une copie:

 Inside default Inside default Inside move with my_int = 0 

Il n’y a aucun avantage réel avec ce type de données d’un mouvement par rapport à une copie. Mais sémantiquement, la différence pourrait être grande si votre type de données était plus “lourd” et disposait d’un moyen de “déplacer” ses membres qui ressemblait davantage à un transfert de propriété d’un pointeur vers une grande structure de données.

Vous avez raison de supposer que l’invocation supplémentaire du constructeur de copie provient de la restructuration interne du vecteur.

Voir cette réponse pour plus de détails: https://stackoverflow.com/a/10368636/3708904

Ou cette réponse pour la raison pour laquelle la construction de copie est nécessaire: https://stackoverflow.com/a/11166959/3708904