HOWTO · C++
Copia un oggetto contenitore vettoriale in C++
Questo articolo illustra come copiare un oggetto contenitore vettoriale in C++.
In questa pagina
Per copiare un std::vector in C++, usa il costruttore di copia quando crei un nuovo vettore e l’assegnazione per copia quando sostituisci il contenuto di un vettore esistente. Per copiare un intervallo, usa il costruttore con intervallo. std::copy è adatto quando l’intervallo di destinazione ha già spazio sufficiente, mentre std::back_inserter aggiunge gli elementi alla destinazione. Queste operazioni copiano gli elementi secondo il rispettivo tipo; non copiano ricorsivamente gli oggetti raggiunti tramite puntatori.
Copiare un vettore con il costruttore di copia o l’operatore di assegnazione
L’inizializzazione per copia crea un nuovo vettore a partire da uno esistente: std::vector<int> copy = source;. L’inizializzazione diretta fa lo stesso usando le parentesi: std::vector<int> copy(source);. Se la destinazione esiste già, usa l’assegnazione: destination = source;. L’assegnazione sostituisce gli elementi della destinazione. Per queste forme, includi <vector>.
Esegui questo esempio:
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#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
Il programma stampa ogni vettore copiato su una riga distinta. Gli elementi std::vector<int> sono valori: modificare un intero in un vettore non modifica l’intero corrispondente nell’altro.
Copiare un intervallo con il costruttore di intervallo di vector
Il costruttore con intervallo accetta un intervallo di iteratori semiaperto: il primo iteratore è incluso, il secondo è escluso. Usa le parentesi per distinguere questo costruttore dalla sintassi delle liste di inizializzazione.
std::vector<int> copy(source.begin(), source.end()); crea una copia dell’intero intervallo. Per un sottointervallo, usa std::vector<int> middle(source.begin() + 1, source.end() - 1); quando questi iteratori identificano gli elementi desiderati.
La prima dichiarazione copia tutti gli elementi; la seconda copia gli elementi interni dell’esempio. Entrambi gli iteratori devono indicare un intervallo valido dello stesso vettore sorgente. È una soluzione pratica quando serve una nuova destinazione per copiare un vettore intero o un suo sottointervallo.
Copiare un intervallo con std::copy
std::copy scrive in un intervallo di destinazione esistente. Includi <algorithm> e, prima della copia, assicurati che la destinazione contenga almeno tanti elementi quanti ne ha l’intervallo sorgente. L’algoritmo non aumenta da solo la dimensione di un vettore.
std::vector<int> destination(source.size());, seguito da std::copy(source.begin(), source.end(), destination.begin());, crea un intervallo di destinazione abbastanza grande per la sorgente.
Per copiare un sottointervallo, passa gli iteratori che ne indicano l’inizio e la fine e predisponi un numero sufficiente di elementi nella destinazione. Gli intervalli sorgente e destinazione non devono sovrapporsi in modo contrario ai requisiti dell’algoritmo.
Aggiungere gli elementi copiati con std::back_inserter
Per aggiungere elementi e lasciare che il vettore cresca quando necessario, includi <algorithm> e <iterator> e usa std::back_inserter:
Per esempio, std::vector<int> destination{10};, seguito da std::copy(source.begin(), source.end(), std::back_inserter(destination));, aggiunge gli elementi della sorgente.
Il valore originale 10 rimane all’inizio, seguito dai valori della sorgente. Questa forma è utile quando la destinazione contiene già degli elementi o quando non si stabilisce in anticipo la sua dimensione finale.
Copiare gli elementi con un ciclo e push_back
Un ciclo rende esplicita l’operazione su ciascun elemento. push_back aggiunge ogni valore copiato in fondo, quindi estende una destinazione che contiene già elementi.
Un ciclo diretto è for (const int& value : source) { destination.push_back(value); }, dopo aver dichiarato std::vector<int> destination;.
Per una copia ordinaria, di solito il costruttore di copia del vettore o il costruttore con intervallo risultano più chiari. Un ciclo è appropriato quando ogni elemento va trasformato o filtrato mentre viene aggiunto. Come le operazioni standard sui vettori, il ciclo copia ogni elemento secondo il suo tipo.
Sostituire o inserire elementi nella destinazione con assign e insert
Usa assign(first, last) quando vuoi sostituire la destinazione con un intervallo. Usa insert(position, first, last) quando vuoi inserire l’intervallo sorgente in una posizione specifica, conservando gli altri elementi della destinazione.
Usa destination.assign(source.begin(), source.end()); per sostituirne il contenuto. Usa destination.insert(destination.begin(), source.begin(), source.end()); per inserire l’intervallo sorgente all’inizio.
La posizione d’inserimento deve essere un iteratore valido della destinazione. Se l’inserimento rialloca la memoria del vettore, tutti gli iteratori, i puntatori e i riferimenti ai suoi elementi diventano non validi. Se non avviene una riallocazione, diventano comunque non validi gli iteratori e i riferimenti in corrispondenza della posizione d’inserimento o successivi; quelli precedenti restano validi. Dopo l’inserimento, recupera di nuovo gli iteratori e i riferimenti interessati.
Capire che cosa copia un vettore
Un std::vector memorizza gli elementi in modo contiguo e gestisce la memoria che li contiene. Copiare un vettore crea un vettore distinto con copie dei suoi elementi. Per std::vector<int>, vengono copiati i valori interi: assegnare in seguito un nuovo valore a un elemento di un vettore non cambia il valore nell’altro.
Per gli altri tipi di elemento, il comportamento dipende dall’operazione di copia definita dal tipo. Per esempio, copiare un vettore di puntatori grezzi copia i valori dei puntatori, senza creare nuovi oggetti puntati. Se gli elementi sono puntatori intelligenti, la copia segue la semantica propria di questi tipi, per esempio la proprietà condivisa di std::shared_ptr. Avere due vettori distinti, quindi, non implica sempre che anche gli oggetti a cui puntano gli elementi siano indipendenti.
Scegliere un metodo per copiare un vettore
Scegli il costruttore di copia o quello con intervallo per creare un nuovo vettore. Scegli l’assegnazione per copia o assign per sostituire una destinazione esistente. Usa insert per inserire un intervallo in una posizione e std::copy con std::back_inserter per aggiungere elementi facendo crescere la destinazione. Usa std::copy con una destinazione dimensionata in precedenza quando vuoi scrivere in un intervallo di elementi già allocato. Un ciclo con push_back è utile quando la copia richiede anche un’elaborazione personalizzata di ciascun elemento.
Per copiare un vettore intero in uno nuovo, preferisci il costruttore di copia; per selezionare gli elementi, usa il costruttore con intervallo. Per un vettore esistente, scegli l’assegnazione o assign per sostituirne il contenuto, insert per collocare gli elementi in una posizione specifica oppure std::back_inserter per aggiungerli in fondo.