HOWTO · C++
Rimuovi elemento dall'array in C++
In questo articolo viene illustrato come rimuovere un elemento da una matrice in C++
In questa pagina
La rimozione di un valore dipende dal contenitore. Un array in stile C mantiene la capacità dichiarata: il codice può spostare i valori e tenere traccia di una lunghezza logica inferiore, oppure copiare i valori da conservare in un altro contenitore. Un std::vector può invece modificare direttamente la propria dimensione. Gli esempi seguenti rimuovono, come indicato, la prima corrispondenza oppure tutte le corrispondenze.
Perché la rimozione di un elemento modifica la dimensione logica di un array
Un array come int values[5] dispone sempre di spazio per cinque interi. Sovrascrivere un elemento con i valori successivi può accorciare la sequenza attiva, ma non modifica la capacità dell’array. Conserva la lunghezza attiva in una variabile separata e usa tale lunghezza quando leggi o stampi la sequenza. Un std::vector<int>, invece, gestisce sia lo spazio di archiviazione sia il numero corrente di elementi.
Spostare gli elementi in un array in stile C
Per rimuovere sul posto la prima occorrenza, cercala, sposta di una posizione a sinistra ogni valore successivo e poi decrementa la lunghezza logica. La capacità dell’array resta cinque; solo i primi length valori appartengono alla sequenza accorciata. Esegui questo esempio:
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#include <iostream>
void remove_first(int values[], int& length, int target) {
int index = 0;
while (index < length && values[index] != target) {
++index;
}
if (index == length) {
return;
}
for (int i = index; i + 1 < length; ++i) {
values[i] = values[i + 1];
}
--length;
}
int main() {
int values[5] = {1, 2, 3, 4, 5};
int length = 5;
remove_first(values, length, 3);
for (int i = 0; i < length; ++i) {
if (i != 0) std::cout << ' ';
std::cout << values[i];
}
std::cout << '\n';
}
1 2 4 5
Questa funzione rimuove solo la prima corrispondenza. Per rimuovere tutte le occorrenze, compatta l’array usando un indice di lettura e uno di scrittura: copia ogni valore diverso da quello da rimuovere nella posizione di scrittura successiva, quindi imposta la lunghezza logica al numero di valori scritti. L’operazione visita ogni elemento una sola volta. Spostare gli elementi dopo ogni singola corrispondenza è più semplice, ma può far spostare più volte gli stessi valori.
Copiare gli elementi rimanenti in un nuovo array
Una destinazione separata lascia intatta la sorgente. Un std::vector è una comoda destinazione di dimensione dinamica e gestisce autonomamente la propria memoria, evitando l’uso manuale di new[] e delete[]. Questo esempio copia ogni valore diverso da 3; se non ci sono corrispondenze, la destinazione contiene semplicemente una copia della sorgente.
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#include <cstddef>
#include <iostream>
#include <vector>
int main() {
const int source[] = {1, 2, 3, 3, 4, 5};
const int length = sizeof(source) / sizeof(source[0]);
std::vector<int> remaining;
for (int i = 0; i < length; ++i) {
if (source[i] != 3) {
remaining.push_back(source[i]);
}
}
for (std::size_t i = 0; i < remaining.size(); ++i) {
if (i != 0) std::cout << ' ';
std::cout << remaining[i];
}
std::cout << '\n';
}
1 2 4 5
La destinazione cresce fino a contenere esattamente i valori conservati. Se un’interfaccia richiede un array dinamico grezzo, conta prima i valori da conservare, alloca quel numero di elementi (che può essere zero) e libera l’allocazione con delete[] quando il proprietario non ne ha più bisogno. Se l’interfaccia non richiede la gestione manuale della memoria, preferisci un contenitore standard.
Rimuovere i valori corrispondenti da std::vector con l’idioma erase-remove
Per un contenitore ridimensionabile, usa std::remove insieme al membro erase del vettore. Includi <algorithm> per l’algoritmo e <vector> per il contenitore. std::remove sposta i valori non corrispondenti verso l’inizio e restituisce un iteratore alla nuova fine logica; non modifica la dimensione del vettore. erase rimuove quindi l’intervallo finale e aggiorna la dimensione. In questo modo vengono rimosse tutte le occorrenze del valore cercato.
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#include <algorithm>
#include <cstddef>
#include <iostream>
#include <vector>
int main() {
std::vector<int> values = {1, 2, 3, 3, 4, 5};
values.erase(std::remove(values.begin(), values.end(), 3), values.end());
for (std::size_t i = 0; i < values.size(); ++i) {
if (i != 0) std::cout << ' ';
std::cout << values[i];
}
std::cout << '\n';
}
1 2 4 5
Usare std::remove con un array a dimensione fissa
std::remove funziona anche sull’intervallo di iteratori di un array in stile C. Riordina i valori e restituisce la nuova fine logica, ma non può ridurre lo spazio di archiviazione dell’array. Quando elabori il risultato, usa l’iteratore restituito come fine dell’intervallo da conservare; non considerare l’intero intervallo originale dell’array come la sequenza accorciata.
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#include <algorithm>
#include <iostream>
#include <iterator>
int main() {
int values[] = {1, 2, 2, 3, 4};
int* logical_end = std::remove(std::begin(values), std::end(values), 2);
for (int* current = std::begin(values); current != logical_end; ++current) {
if (current != std::begin(values)) std::cout << ' ';
std::cout << *current;
}
std::cout << '\n';
}
1 3 4
Gli elementi compresi tra logical_end e la fine effettiva dell’array restano oggetti validi, ma l’algoritmo non specifica i loro valori. Non stamparli e non farci affidamento. Se ti serve una sequenza fisicamente più corta, copia il prefisso che termina in logical_end in una destinazione di dimensioni adeguate.
Scegliere un approccio e gestire i casi limite
Usa lo spostamento quando devi aggiornare sul posto un array a dimensione fissa e puoi tenere traccia separatamente della sua lunghezza logica. Scegli una nuova destinazione quando l’originale deve restare intatto o il risultato deve avere uno spazio di archiviazione indipendente. Usa std::vector quando la raccolta deve poter aumentare o diminuire di dimensione; il suo idioma erase-remove è in genere il modo più chiaro per eliminare tutti i valori corrispondenti.
Specifica chiaramente se l’operazione rimuove la prima corrispondenza o tutte. Se non viene trovato alcun valore, la sequenza resta invariata. Dopo la rimozione, un array di un solo elemento può avere una lunghezza logica pari a zero, mentre la sua capacità dichiarata resta uno. Per un vettore vuoto, std::remove restituisce begin() e la cancellazione dell’intervallo vuoto risultante lo lascia vuoto. Non servono accorgimenti speciali per l’allocazione, purché lunghezze e intervalli di iteratori siano gestiti con coerenza.