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.