2010-02-16 16 views
115

Después de muchas investigaciones con valgrind, he llegado a la conclusión de que std :: vector hace una copia de un objeto que desea push_back.¿Está std :: vector copiando los objetos con push_back?

¿Es eso realmente cierto? Un vector no puede mantener una referencia o un puntero de un objeto sin una copia?

Gracias

+15

Este es un principio básico de C++. Los objetos son valores. La asignación hace una copia. No son posibles dos variables que hagan referencia al mismo objeto a menos que modifique el tipo con '*' o '&' para hacer un puntero o referencia. –

+4

@DanielEarwicker push_back realmente toma una referencia. No está claro solo por la firma que hará una copia. –

+2

@BrianGordon - ¡No lo dice! De ahí la necesidad del principio rector. Aún así, podemos deducir algo de la firma de 'push_back': toma' const & '. O arroja el valor (inútil), o hay un método de recuperación. Así que observamos la firma de 'back', y devuelve plain' & ', por lo que o bien se copió el valor original o se descartó silenciosamente el' const' (muy malo: comportamiento potencialmente indefinido). Asumiendo que los diseñadores de 'vector' eran racionales (' vector 'no obstante) concluimos que hace copias. –

Respuesta

124

Sí, std::vector<T>::push_back() crea una copia del argumento y lo almacena en el vector. Si desea almacenar punteros a objetos en su vector, cree un std::vector<whatever*> en lugar de std::vector<whatever>.

Sin embargo, debe asegurarse de que los objetos a los que hacen referencia los punteros sigan siendo válidos mientras el vector contiene una referencia a ellos (los punteros inteligentes que utilizan el modismo RAII resuelven el problema).

+5

+1 por mencionar la necesidad de punteros inteligentes –

+0

También me gustaría señalar que, si usa punteros crudos, ahora es responsable de limpiarlos después de ellos. No hay una buena razón para hacer esto (no es una que se me ocurra), siempre debes usar un puntero inteligente. –

+1

Dicho esto, no debe usar std :: auto_ptr en contenedores stl, para obtener más información: [why-is-it-wrong-to-use-stdauto-ptr-with-standard-containers] (http: // stackoverflow. com/questions/111478/why-is-it-wrong-to-use-stdauto-ptr-with-standard-containers) – OriginalCliche

32

Sí, std::vector almacena copias. ¿Cómo debe saber vector cuáles son los tiempos de vida esperados de sus objetos?

Si desea transferir o compartir la propiedad de los objetos utilizan punteros, punteros inteligentes, posiblemente como shared_ptr (que se encuentra en Boost o TR1) para facilitar la gestión de recursos.

+2

aprende a usar shared_ptr: hacen exactamente lo que quieres. Mi idioma favorito es typedef boost :: shared_ptr FooPtr; Luego haga contenedores de FooPtrs – pm100

+3

@ pm100 - ¿Conoce 'boost :: ptr_vector'? – Manuel

+0

Según http://www.boost.org/doc/libs/1_42_0/libs/ptr_container/doc/ptr_vector.html, es un puntero sin formato, no inteligente. Peligroso. –

13

std :: vector siempre hace una copia de lo que se almacena en el vector.

Si mantiene un vector de punteros, hará una copia del puntero, pero no la instancia a la que apunta el puntero. Si está tratando con objetos grandes, puede (y probablemente debería) usar siempre un vector de punteros. A menudo, utilizar un vector de punteros inteligentes de un tipo apropiado es bueno por motivos de seguridad, ya que manejar la vida útil del objeto y la administración de la memoria puede ser complicado.

+3

no depende del tipo. Siempre hace una copia. Si su puntero es una copia del puntero – pm100

+0

Ambos tienen razón. Técnicamente, sí, siempre hace una copia. Prácticamente, si le pasa un puntero al objeto, copia el puntero, no el objeto. De forma segura, debe usar un puntero inteligente apropiado. –

+1

Sí, siempre está copiando - Sin embargo, el "objeto" al que se refiere OP es muy probablemente una clase o estructura, por lo que me refería a si copiar el "Objeto" depende de la definición. Mal redactado, sin embargo. –

3

Std :: vector no solo hace una copia de lo que está retrocediendo, sino que la definición de la colección establece que lo hará y que no puede usar objetos sin la semántica de copia correcta dentro de un vector . Entonces, por ejemplo, no usa auto_ptr en un vector.

0

si no quiere las copias; entonces la mejor manera es usar un vector de puntero (u otra estructura que sirva para el mismo objetivo). si quiere las copias; use directamente push_back(). no tiene otra opción.

+1

Una nota sobre vectores de puntero: vector > es mucho más seguro que el vector y shared_ptr es parte del estándar del año pasado. –

-1

¿Por qué fue necesario investigar mucho para averiguarlo? ¡Pruébelo usted mismo con un código simple, p.

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

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

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

Si "hola mundo" se imprime, el objeto debe haber sido copiado

+3

Esto no constituye una prueba. Si el objeto no se ha copiado, su última declaración sería un comportamiento indefinido y podría imprimirse hola. – Mat

+4

la prueba correcta estaría modificando uno de los dos después de la inserción. Si fueran el mismo objeto (si el vector almacenara una referencia), ambos serían modificados. –

2

relevante en C++ 11 es el emplace familia de funciones miembros, que le permiten transferir la propiedad de objetos moviéndolos en contenedores.

El idioma de uso se vería

std::vector<Object> objs; 

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

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

La medida para el objeto lvalue es importante ya que de lo contrario sería enviado como referencia o referencia constante y el movimiento constructor no sería llamado.

11

De C++ 11 en adelante, todos los contenedores estándar (std::vector, std::map, etc) semántica de soporte se mueven, lo que significa que ahora puede pasar a rvalues ​​contenedores estándar y evitar una copia:

// 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"); 

alternativa, puede utilizar varios punteros inteligentes para conseguir su mayoría el mismo efecto:

std::unique_ptr ejemplo

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 ejemplo

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. 
+0

Tenga en cuenta que 'std :: make_unique' está (molestamente) disponible solo en C++ 14 y superiores. Asegúrese de decirle a su compilador que configure su conformidad estándar en consecuencia si desea compilar estos ejemplos. –

+0

En 5a puede usar 'auto pFoo =' para evitar la repetición; y todos los moldes 'std :: string' se pueden eliminar (hay una conversión implícita de literales de cadena a' std :: string') –

+1

@ user465139 'make_unique' se puede implementar fácilmente en C++ 11, por lo que solo es un ligera molestia para alguien atascado con un compilador de C++ 11 –

Cuestiones relacionadas