HOWTO · C++
Copiar un objeto contenedor vectorial en C++
Este artículo muestra cómo copiar un objeto contenedor vectorial en C++.
En esta página
Para copiar un std::vector de C++, usa su constructor de copia al crear un vector nuevo y la asignación por copia para reemplazar el contenido de uno existente. Para copiar un rango concreto, usa el constructor de rango. std::copy funciona cuando el rango de destino ya tiene espacio suficiente, mientras que std::back_inserter añade elementos al destino. Estas operaciones copian los elementos según sus tipos; no copian recursivamente los objetos a los que se accede mediante punteros.
Copiar un vector con su constructor de copia o su operador de asignación
La inicialización por copia construye un vector nuevo a partir de uno existente: std::vector<int> copy = source;. La inicialización directa hace lo mismo con paréntesis: std::vector<int> copy(source);. Si el destino ya existe, usa la asignación: destination = source;. La asignación reemplaza los elementos del destino. Incluye <vector> para estas formas.
Ejecuta este ejemplo:
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
#include <cstddef>
#include <iostream>
#include <vector>
int main() {
const std::vector<int> source{2, 4, 6};
std::vector<int> copy = source;
std::vector<int> destination{9};
destination = source;
for (std::size_t i = 0; i < copy.size(); ++i) {
if (i != 0) {
std::cout << ' ';
}
std::cout << copy[i];
}
std::cout << '\n';
for (std::size_t i = 0; i < destination.size(); ++i) {
if (i != 0) {
std::cout << ' ';
}
std::cout << destination[i];
}
std::cout << '\n';
}
2 4 6
2 4 6
Este programa imprime cada vector copiado en una línea distinta. Los elementos de su std::vector<int> son valores, así que cambiar un entero en un vector no cambia el entero correspondiente en otro.
Copiar un rango con el constructor de rango de vector
El constructor de rango recibe un rango de iteradores semiabierto: incluye el primer iterador y excluye el segundo. Usa paréntesis para distinguir este constructor de la sintaxis de lista de inicializadores.
std::vector<int> copy(source.begin(), source.end()); crea una copia de todo el rango. Para un subrango, usa std::vector<int> middle(source.begin() + 1, source.end() - 1); cuando esos iteradores delimiten los elementos deseados.
La primera declaración copia todos los elementos; la segunda copia el rango interior de este ejemplo. Ambos iteradores deben delimitar un rango válido dentro del mismo vector de origen. Es una opción práctica cuando necesitas un destino nuevo para copiar un vector completo o un subrango.
Copiar un rango con std::copy
std::copy escribe en un rango de destino existente. Incluye <algorithm> y, antes de copiar, asegúrate de que el destino tenga al menos tantos elementos como el rango de origen. El algoritmo no aumenta el tamaño de un vector por sí solo.
std::vector<int> destination(source.size()); seguido de std::copy(source.begin(), source.end(), destination.begin()); proporciona un rango de destino con espacio suficiente para el origen.
Para copiar un subrango, pasa sus iteradores de inicio y fin y reserva suficientes elementos en el destino para ese rango. Los rangos de origen y destino no deben solaparse de una forma que incumpla los requisitos del algoritmo.
Añadir elementos copiados con std::back_inserter
Para añadir elementos y permitir que el vector crezca según sea necesario, incluye <algorithm> y <iterator>, y usa std::back_inserter:
Por ejemplo, std::vector<int> destination{10}; seguido de std::copy(source.begin(), source.end(), std::back_inserter(destination)); añade los elementos del origen.
El 10 original permanece al principio, seguido de los valores del origen. Esta forma resulta útil cuando el destino ya contiene elementos o cuando no se establece de antemano su tamaño final.
Copiar elementos con un bucle y push_back
Un bucle hace explícita la operación que se realiza con cada elemento. push_back añade cada valor copiado al final, por lo que amplía un destino existente.
Un bucle sencillo es for (const int& value : source) { destination.push_back(value); }, después de declarar std::vector<int> destination;.
Para una copia normal, el constructor de copia o el constructor de rango de vector suelen ser más claros. Un bucle es adecuado cuando cada elemento debe transformarse o filtrarse al añadirlo. Igual que las operaciones estándar de vector, el bucle copia cada elemento según su tipo.
Reemplazar o insertar elementos en el destino con assign e insert
Usa assign(first, last) cuando quieras reemplazar el contenido del destino por un rango. Usa insert(position, first, last) cuando quieras colocar el rango de origen en una posición y conservar los demás elementos del destino.
Usa destination.assign(source.begin(), source.end()); para reemplazar su contenido. Usa destination.insert(destination.begin(), source.begin(), source.end()); para insertar el rango de origen al principio.
La posición de inserción debe ser un iterador válido del destino. Si la inserción reasigna el almacenamiento del vector, todos los iteradores, punteros y referencias a sus elementos quedan invalidados. Si no hay reasignación, también quedan invalidados los iteradores y referencias situados en la posición de inserción o después de ella; los anteriores siguen siendo válidos. Después de insertar, vuelve a obtener los iteradores y referencias afectados.
Entender qué duplica una copia de vector
Un std::vector almacena sus elementos de forma contigua y administra ese almacenamiento. Al copiar un vector, se crea otro vector con copias de sus elementos. En un std::vector<int>, se copian los valores enteros, por lo que una asignación posterior a un elemento de un vector no modifica el valor del otro.
El comportamiento con otros tipos de elementos depende de la operación de copia de cada tipo. Por ejemplo, copiar un vector de punteros sin procesar copia los valores de los punteros; no crea nuevos objetos apuntados. Si los elementos son punteros inteligentes, la copia sigue su propia semántica, como la propiedad compartida de std::shared_ptr. Por tanto, tener otro vector no siempre significa que los objetos a los que apuntan sus elementos sean independientes.
Elegir un método para copiar un vector
Elige el constructor de copia o el constructor de rango para crear un vector nuevo. Elige la asignación por copia o assign para reemplazar un destino existente. Elige insert para añadir un rango en una posición, y std::copy con std::back_inserter para añadir elementos mientras crece el destino. Usa std::copy con un destino dimensionado cuando quieras escribir en un rango de elementos ya asignado. Un bucle con push_back es útil cuando la copia también requiere un procesamiento personalizado de cada elemento.
Para hacer una copia completa nueva, suele ser preferible el constructor de copia; usa un constructor de rango para seleccionar elementos. Si el vector ya existe, elige la asignación o assign para reemplazar su contenido, insert para colocar elementos en una posición o std::back_inserter para añadirlos al final.