HOWTO · C++
Come creare un vettore di vettori in C++
Scopri come creare, riempire e modificare un vettore di vettori in C++.
In questa pagina
Un vettore di vettori contiene oggetti std::vector come righe all’interno di un std::vector esterno. Per crearne uno con un numero noto di righe e colonne, inizializza il vettore esterno con il numero di righe e usa, come valore iniziale di ciascuna riga, un vettore interno con il numero di colonne e il valore iniziale desiderato. Gli esempi mostrano come creare una griglia con dimensioni definite, riempirla con valori di esempio e modificarne gli elementi. Questa struttura è utile quando ogni riga deve essere un vettore e il programma determina il numero di righe o colonne.
Creare un vettore di vettori in C++ con il costruttore che specifica dimensione e valore
Il costruttore che accetta dimensione e valore rende esplicite sia le dimensioni sia il contenuto iniziale. Per esempio, std::vector<std::vector<int>> grid(rows, std::vector<int>(columns, 0)); crea rows vettori interni, ciascuno con columns interi inizializzati a zero. Il vettore interno viene costruito una volta e usato come valore con cui inizializzare le righe del vettore esterno. Si tratta quindi di una costruzione con dimensione e valore iniziale, non di una costruzione predefinita.
Questo esempio crea quattro righe di sei interi. Gli indici dei vettori partono da zero: [2][2] seleziona la terza riga e la terza colonna. Le chiamate annidate a .at() selezionano la quarta riga e la quarta colonna. .at() verifica ciascun indice rispetto alla dimensione del vettore corrispondente e segnala un accesso fuori intervallo se l’indice non identifica un elemento; l’accesso con parentesi quadre è più conciso quando gli indici sono validi.
#include <iostream>
#include <vector>
int main() {
constexpr int LENGTH = 4;
constexpr int WIDTH = 6;
std::vector<std::vector<int>> grid(LENGTH, std::vector<int>(WIDTH, 0));
grid[2][2] = 12;
std::cout << grid[2][2] << '\n';
grid.at(3).at(3) = 99;
std::cout << grid[3][3] << '\n';
return 0;
}
12
99
L’output è determinato dalle due assegnazioni. Usa gli stessi indici per le dimensioni della riga e della colonna: il vettore esterno seleziona una riga e il vettore interno di quella riga seleziona una colonna. Se le dimensioni non sono fisse, il numero di righe e la dimensione di ciascuna riga possono invece provenire dai dati del programma.
Riempire un vettore di vettori in C++ con valori di esempio usando la funzione rand
Una volta creata la griglia, due cicli annidati possono visitare ogni intero e assegnargli un valore di esempio. Il programma seguente inizializza rand usando l’ora corrente e applica % 100 per ottenere valori da 0 a 99. I valori stampati cambiano da un’esecuzione all’altra, quindi non esiste un unico output da riportare. L’esempio include <cstdlib> per rand e srand e <ctime> per std::time.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
int main() {
constexpr int LENGTH = 4;
constexpr int WIDTH = 6;
std::vector<std::vector<int>> grid(LENGTH, std::vector<int>(WIDTH, 0));
std::srand(static_cast<unsigned int>(std::time(nullptr)));
for (auto& row : grid) {
for (int& value : row) {
value = std::rand() % 100;
std::cout << value << ' ';
}
std::cout << '\n';
}
return 0;
}
Questi valori servono come dati di esempio per dimostrazioni e attività semplici non sensibili. rand non è adatta alla crittografia. Inoltre, l’operazione resto può introdurre una distorsione, a meno che il numero di valori possibili del generatore non sia divisibile per 100. Per simulazioni o applicazioni che richiedono proprietà distributive migliori, scegli un generatore casuale adatto a tale requisito: questo breve esempio non è un generatore di uso generale.
Modificare ogni elemento di un vettore di vettori in C++ con cicli annidati basati su intervalli
Un ciclo basato su intervalli visita ogni riga e un ciclo annidato visita ogni intero della riga. In int& value, & fa sì che value sia un riferimento all’elemento memorizzato, perciò un’assegnazione modifica il vettore. Senza il riferimento, la variabile del ciclo sarebbe una copia e aggiornarla non cambierebbe l’intero memorizzato. Questo programma stampa una griglia prima e dopo aver moltiplicato ogni elemento per tre.
#include <iostream>
#include <vector>
void print(const std::vector<std::vector<int>>& grid) {
for (const auto& row : grid) {
for (int value : row) {
std::cout << value << ' ';
}
std::cout << '\n';
}
}
int main() {
std::vector<std::vector<int>> grid{{1, 2, 3}, {4, 5, 6}};
std::cout << "Before:\n";
print(grid);
for (auto& row : grid) {
for (int& value : row) {
value *= 3;
}
}
std::cout << "After:\n";
print(grid);
return 0;
}
Ogni vettore interno gestisce una propria sequenza di interi, mentre il vettore esterno memorizza i vettori delle righe. Gli interi di una stessa riga sono contigui, ma l’intera struttura annidata non costituisce un unico blocco bidimensionale contiguo. Questa è una descrizione del layout di memoria, non un’affermazione generale sulle prestazioni: queste dipendono dall’operazione e dal carico di lavoro. Un vettore annidato è utile quando le righe sono naturalmente separate o richiedono le normali operazioni di un vettore. Se il requisito è un unico blocco contiguo, scegli una rappresentazione progettata per quel layout.