web-dev-qa-db-fra.com

Std :: vector copie-t-il les objets avec un Push_back?

Après de nombreuses investigations avec valgrind, j'ai conclu que std :: vector faisait une copie d'un objet que vous voulez Push_back.

Est-ce vraiment vrai? Un vecteur ne peut pas garder une référence ou le pointeur d'un objet sans copie?!

Merci

152
benlaug

Oui, std::vector<T>::Push_back() crée une copie de l'argument et le stocke dans le vecteur. Si vous souhaitez stocker des pointeurs sur des objets de votre vecteur, créez un fichier std::vector<whatever*> au lieu de std::vector<whatever>.

Cependant, vous devez vous assurer que les objets référencés par les pointeurs restent valides tant que le vecteur en contient une référence (les pointeurs intelligents utilisant l'idiome RAII résolvent le problème).

167
Alexander Gessler

Oui, std::vector stocke les copies. Comment vector devrait-il savoir quelle est la durée de vie attendue de vos objets?

Si vous souhaitez transférer ou partager la propriété des objets, utilisez des pointeurs, éventuellement des pointeurs intelligents tels que shared_ptr (trouvé dans Boost ou TR1 ) pour faciliter la gestion des ressources.

33
Georg Fritzsche

À partir de C++ 11, tous les conteneurs standard (std::vector, std::map, etc.) prend en charge la sémantique des déplacements, ce qui signifie que vous pouvez désormais transmettre des valeurs à des conteneurs standard et éviter une copie:

// Example object class.
class object
{
private:
    int             m_val1;
    std::string     m_val2;

public:
    // Constructor for object class.
    object(int val1, std::string &&val2) :
        m_val1(val1),
        m_val2(std::move(val2))
    {

    }
};

std::vector<object> myList;

// #1 Copy into the vector.
object foo1(1, "foo");
myList.Push_back(foo1);

// #2 Move into the vector (no copy).
object foo2(1024, "bar");
myList.Push_back(std::move(foo2));

// #3 Move temporary into vector (no copy).
myList.Push_back(object(453, "baz"));

// #4 Create instance of object directly inside the vector (no copy, no move).
myList.emplace_back(453, "qux");

Sinon, vous pouvez utiliser divers pointeurs intelligents pour obtenir principalement le même effet:

std::unique_ptr _ exemple

std::vector<std::unique_ptr<object>> myPtrList;

// #5a unique_ptr can only ever be moved.
auto pFoo = std::make_unique<object>(1, "foo");
myPtrList.Push_back(std::move(pFoo));

// #5b unique_ptr can only ever be moved.
myPtrList.Push_back(std::make_unique<object>(1, "foo"));

std::shared_ptr _ exemple

std::vector<std::shared_ptr<object>> objectPtrList2;

// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector
// value and the local copy simultaneously.
auto pFooShared = std::make_shared<object>(1, "foo");
objectPtrList2.Push_back(pFooShared);
// Pointer to object stored in the vector, but pFooShared is still valid.
24
Karl Nicoll

std :: vector fait toujours une copie de tout ce qui est stocké dans le vecteur.

Si vous conservez un vecteur de pointeurs, il en fera une copie, mais pas l'instance sur laquelle il pointe. Si vous utilisez des objets volumineux, vous pouvez (et devriez probablement) toujours utiliser un vecteur de pointeurs. L'utilisation d'un vecteur de pointeurs intelligents d'un type approprié est souvent utile pour des raisons de sécurité, car la gestion de la durée de vie des objets et la gestion de la mémoire peuvent s'avérer délicates.

15
Reed Copsey

Non seulement std :: vector crée une copie de tout ce que vous repoussez, mais la définition de la collection indique qu'elle le fera et que vous ne pouvez pas utiliser d'objets sans la sémantique de copie correcte dans un vecteur. Ainsi, par exemple, vous n'utilisez pas auto_ptr dans un vecteur.

3
Liz Albin

La famille emplace de fonctions membres est pertinente dans C++ 11 et vous permet de transférer la propriété des objets en les déplaçant dans des conteneurs.

Le langage d'usage ressemblerait à

std::vector<Object> objs;

Object l_value_obj { /* initialize */ };
// use object here...

objs.emplace_back(std::move(l_value_obj));

Le déplacement de l'objet lvalue est important car sinon, il serait transmis en tant que référence ou référence constante et le constructeur du déplacement ne serait pas appelé.

2
LemonPi

Pourquoi at-il fallu beaucoup d’investigations de la part de valgrind pour le découvrir! Prouvez-le vous-même avec un code simple, par exemple.

std::vector<std::string> vec;

{
      std::string obj("hello world");
      vec.Push_pack(obj);
}

std::cout << vec[0] << std::endl;  

Si "hello world" est imprimé, l'objet doit avoir été copié

0
NexusSquared

si vous ne voulez pas les copies; Le meilleur moyen consiste alors à utiliser un vecteur de pointeur (ou une autre structure servant le même objectif). si vous voulez les copies; utilisez directement Push_back (). vous n'avez pas d'autre choix.

0
rahmivolkan