I template (modelli di funzione) sono particolari funzioni che possono operare con tipi generici. In questo modo si crea lo schema di una funzione le cui operazioni possono essere adattate a più di un tipo, o classe, senza dover riscrivere l'intero corpo della funzione per ciascun tipo.
In C++ questo può essere ottenuto usando i parametri formali del template.
Un parametro formale del template è uno speciale parametro che può essere usato per passare un tipo come parametro ad una funzione, allo stesso modo in cui viene passato un parametro effettivo ad una funzione. I template di funzione possono usare il tipo ricevuto come parametro come se fosse un qualsiasi tipo predefinito.
Il formato della dichiarazione di un template di funzione che si aspetta di ricevere, al momento della chiamata, sia il tipo restituito sia il tipo dei parametri effettivi è:
template <class identificatore> dichiarazione di funzione;
Ad esempio un template di funzione che restituisce il maggiore tra due elementi potrebbe essere dichiarato così:
template <class Tipo> Tipo MassimoTra(Tipo a, Tipo b) { return (a>b ? a : b); }
MassimoTra è un template di funzione con Tipo come parametro formale. Questo parametro rappresenta un tipo che non è stato ancora specificato ma che può essere usato nel template della funzione come se fosse un tipo predefinito. La funzione deve restituire il maggiore tra due parametri di questo tipo non ancora specificato.
La chiamata di questo template di funzione deve rispettare la seguente sintassi:
Nome_funzione <Tipo> (parametri);
Ad esempio, per chiamare la funzione MaggioreTra per ottenere il più grande tra due caratteri x e y, basta sostituire Tipo con char:
char x, y; MassimoTra <char> (x, y);
Quando il compilatore raggiunge questa chiamata al template della funzione, usa lo schema del template per generare automaticamente una funzione in cui sono stati sostituiti i termini Tipo con il tipo passato come parametro effettivo (char, in questo caso) e poi la chiama. Questa operazione è compiuta dal compilatore e non è visibile al programmatore. Il seguente esempio illustra un programma in cui è presente la dichiarazione e l'uso del template:
#include <iostream> using namespace std; template <class Tipo> Tipo MassimoTra(Tipo a, Tipo b) { return (a>b ? a : b); } int main() { int i=5, j=6, k; long m=10, n=5, p; k = MassimoTra <int> (i, j); cout << k << " maggiore tra " << i << " e " << j << endl; p = MassimoTra <long> (m, n); cout << p << " maggiore tra " << m << " e " << n << endl; return 0; }
Nell'esempio precedente si è usato due volte il nome del template della funzione MassimoTra. La prima volta con il parametro di tipo int e la seconda volta con il parametro di tipo long. Il compilatore ha creato e poi chiamato l'appropriata versione della funzione.
All'interno del template della funzione MassimoTra, si potrebbe scrivere "Tipo risultato;" in cui Tipo è usato per dichiarare una nuova variabile di quel tipo. Quindi, risultato sarebbe una variabile dello stesso tipo dei parametri a e b quando la funzione associata al template verrà creata con uno specifico tipo.
In questo caso particolare il tipo generico Tipo è usato come parametro per MassimoTra e il compilatore può determinare automaticamente quale tipo di dato deve creare senza doverlo specificare esplicitamente tra i segni < e >, come è stato fatto specificando <int> e <long>. Infatti, si sarebbe potuto scrivere:
int i, j; MassimoTra (i, j);
Poichè sia i che j sono di tipo int, il compilatore può automaticamente dedurre che il parametro del template può essere solo int. Questo metodo implicito produce esattamente lo stesso risultato.
Poichè il template della funzione include un solo parametro formale del template (class T) e il template della funzione stessa accetta due parametri, entrambi di questo stesso tipo T, non è possibile chiamare il template della funzione passando due parametri di tipo differente tra loro, come nel seguente caso:
int i; long m; k = MassimoTra (i, m); // errore i e m sono di tipo diverso tra loro
Per poter passare parametri di tipi diversi, il template della funzione deve accettare più di un tipo come parametro, basta specificare nomi diversi per i parametri formale del template tra i segni < e >. Per esempio:
template <class T, class U> T MinimoTra (T a, U b) { return (a<b ? a : b); }
In questo caso il template della funzione MinimoTra() accetta due parametri di tipi diversi e restituisce un valore dello stesso tipo del primo parametro (T) che viene passato. Ad esempio, dopo la dichiarazione si potrebbe chiamare la funzione MinimoTra() con la seguente espressione:
int i, j; long m; i = MinimoTra<int, long> (j, m);o semplicemente:
i = MinimoTra (j, m);
persino se j ed m hanno tipo differente, perchè il compilatore lo può dedurre.
Si possono scrivere classi parametriche (template di classi) allo scopo di dichiarare campi membro che usino i parametri formali del template come tipo. Ad esempio:
template <class T> class coppia { T valori[2]; public: coppia (T primo, T secondo) { valori[0]=primo; valori[1]=secondo; } };
La classe che è stata appena definita memorizza due valori di un certo tipo. Ad esempio, per dichiarare un oggetto di classe coppia, per memorizzare i due valori interi 115 e 36 si deve richiamare il costruttore in questo modo:
coppia <int> coordinate(115, 36);
la stessa classe può essere usata per creare un oggetto che memorizzi una coppia di numeri di un qualsiasi altro tipo:
coppia <double> numeri (3.0, 2.18);
L'unica funzione membro della classe è stata dichiarata inline, nella stessa dichiarazione della classe. Quando si definisce una funzione membro all'esterno della classe parametrica, bisogna sempre usare il prefisso template <.>:
il file coppia.h
#include <iostream> using namespace std; template <class T> class coppia { T a, b; public: coppia (T primo, T secondo) { a = primo; b = secondo; } T MassimoTra(); };
Il file coppia.cpp
template <class T> T coppia<T>::MassimoTra() { T max; max = a>b ? a : b; return max; }
Uso della classe
int main () { coppia <int> coordinate(100, 75); cout << "Massimo: " << coordinate.MassimoTra(); return 0; }
Notare la sintassi usata per scrivere la definizione della funzione membro MassimoTra:
template <class T> T coppia<T>::MassimoTra()
In questa dichiarazione ci sono tre T: La prima è il parametro del template. La seconda T si riferisce al tipo restituito dalla funzione. Anche la terza T (quella tra i segni di minore e maggiore) è richiesta: specifica che questo parametro formale del template della funzione è anche il parametro della classe template.
Se si vuole definire una implementazione differente per un template, che viene usata quando si passa uno specificato tipo come parametro del template, si può dichiarare una specializzazione di quel template.
Ad esempio, si consideri una semplice classe chiamata contenitore che può memorizzare un elemento di un tipo qualsiasi, ed ha una funzione membro chiamata incrementa, che ne incrementa il valore. Ci si accorge che quando si memorizza un elemento di tipo char dovrebbe essere più conveniente avere un'implementazione completamente diversa, cioè si preferisce applicare la trasformazione in maiuscolo invece dell'incremento, quindi si decide di dichiarare una classe parametrica specializzata per tale tipo:
#include <iostream> using namespace std; template <class T> class contenitore { T elemento; public: contenitore (T arg) { elemento=arg; } T incrementa () { return ++elemento; } }; template <> class contenitore <char> { char elemento; public: contenitore (char arg) { elemento=arg; } char maiuscolo () { if ((elemento>='a')&&(elemento>='z')) elemento+='A'-'a'; return elemento; } }; int main () { contenitore <int> numero(7); contenitore <char> lettera('j'); cout << numero.incrementa() << endl; cout << lettera.maiuscolo() << endl; return 0; }
Questa è la sintassi usata nella specializzazione della classe parametrica:
template <> class contenitore <char> { ... };
Notare che il nome della classe parametrica è preceduto da un elenco vuoto di parametri dopo il termine template. Questo è il modo per specificare che si sta definendo un template specializzato.
Ma altrettanto importante da notare è il parametro <char>, della specializzazione della classe, che segue il nome della classe parametrica. Questo parametro stesso individua il tipo che differenzia il comportamento speciale quando si passa un char. Notare la differenza tra la classe parametrica generica e la classe specializzata:
template <class T> class contenitore { ... }; template <> class contenitore <char> { ... };
La prima linea è il template della classe generica, la seconda è il template della classe specializzata.
Quando si dichiara una specializzazione per una classe parametrica bisogna definire anche tutti i suoi campi membro, compresi quelli esattamente uguali nella classe parametrica generica, perchè non c'è ereditarietà tra i membri della classe parametrica generica e quelli della classe specializzata.
A parte i parametri del template che sono preceduti dai termini class o typename, che rappresentano dei tipi, i template possono avere anche parametri di un tipo predefinito. Per un esempio si osservi la seguente classe, usata per contenere sequenze di elementi:
#include <iostream> using namespace std; template <class T, int N> class sequenza { T blocco[N]; public: void scrivi (int x, T value); T leggi (int x); }; template <class T, int N> void sequenza<T, N>::scrivi (int x, T valore) { blocco[x] = valore; } template <class T, int N> T sequenza<T, N>::leggi(int x) { return blocco[x]; } int main () { sequenza <int, 5> valoriInt; sequenza <double,5> valoriDouble; valoriInt.scrivi (0, 100); valoriDouble.scrivi (3, 3.1416); cout << valoriInt.leggi(0) << endl; cout << valoriDouble.leggi(3) << endl; return 0; }
Si possono anche impostare valori o tipi di default per le classi parametriche. Ad esempio se la definizione della classe precedente fosse stata:
template <class T=char, int N=10> class sequenza {..};
Si potrebbero creare oggetti usando i parametri di default del template dichiarando: sequenza<> seq;
Che dovrebbe essere equivalente a: sequenza<char, 10> seq;
Un contenitore è un raccoglitore che memorizza una collezione di oggetti (i suoi elementi). I contenitori vengono implementati come classi parametriche, perchè offrono una grande flessibilità nei tipi degli elementi.
Il contenitore si occupa di gestire lo spazio di memoria per i suoi elementi e fornisce le funzioni membro per l'accesso agli elementi, o direttamente oppure tramite oggetti iterator (riferimenti a oggetti, cioè hanno proprietà simili ai puntatori).
I contenitori riproducono, sotto forma di classi, le più comuni strutture dati usate nella programmazione, offrendo accanto ai dati anche le operazioni di accesso: array (vector), code (queue), stack (stack), liste concatenate (list), alberi (set), array associativi (map).
Molti contenitori hanno diverse funzioni membro in comune e ne condividono le funzionalità. La decisione sulla scelta del tipo di contenitore da usare per un caso specifico non dipende solo dalla funzionalità offerta dal contenitore, ma anche dall'efficienza dei membri (complessità).
In C++, un iterator è un qualsiasi oggetto che, puntando ad un certo elemento in un range di elementi (come ad esempio un array o un contenitore), riesce ad iterare attraverso gli elementi di quell'insieme, usando alcuni operatori, tra questi l'incremento (++) e il dereferimento (*).
La forma più ovvia di iteratore è il puntatore: Un puntatore può puntare agli elementi di un array, e può scandire tutta la lista dei suoi elementi usando l'operatore incremento (++). Esistono anche altre forme di accesso iterativo. Ogni tipo contenitore (ad esempio vector) ha un tipo iterator specifico, progettato per accedere in modo efficiente agli elementi del contenitore.
Mentre un puntatore è una forma di iteratore, non tutti gli iteratori offrono la stessa funzionalità di un puntatore. Per distinguere i requisiti che dovrà possedere un iteratore usato in uno specifico algoritmo, esistono cinque diverse categorie di iteratori:
Gli iteratori input e output sono i tipi più limitati, specializzati nell'eseguire solo operazioni sequenziali di ingresso uscita.
Gli iteratori forward possono accedere ad un insieme di elementi sequenzialmente, in una sola direzione.
Gli iteratori bidirezionali possono accedere ad un insieme di elementi in entrambe le direzioni.
Gli iteratori ad accesso random, oltre all'accesso bidirezionale, consentono di accedere ad elementi che occupano una specificata posizione all'interno dell'insieme.
Questo template della classe base può essere usato per derivare altre classi Iterator. Questa classe base fornisce solo alcuni tipi membro, che infatti non si richiede che siano presenti in qualsiasi tipo di Iterator, ma potrebbero essere utili per derivare l'appropriata classe.
È definito come:
template <class Categoria, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>
Solo i primi due parametric sono obbligatori, i restanti sono opzionali, se non vengono specificati si usano i tipi di default.
il parametro Categoria specifica la Categoria a cui appartiene l'iterator. Deve essere uno dei seguenti:
input_iterator_tag - gli Input iterator sono iteratori progettati specificamente per le operazioni sequenziali di input, in cui ogni valore puntato dell'iteratore viene letto solo una volta, dopo di che l'iteratore viene incrementato per preparare la successiva operazione di input. Ogni contenitore definisce un proprio tipo di iteratore, in grado di iterare per accedere ai suoi elementi.
output_iterator_tag - gli Output iterator sono iteratori progettati per le operazioni di output, dove ogni elemento puntato dall'iteratore viene aggiornato con un nuovo valore e poi l'iteratore viene incrementato per prepararsi a scrivere nel successivo elemento del contenitore. Ogni contenitore definisce un proprio tipo di iteratore, in grado di iterare per accedere ai suoi elementi.
forward_iterator_tag - I forward iterator sono progettati per l'accesso sequenziale, in cui l'algoritmo prevede l'accesso a tutti gli elementi nel range dal primo all'ultimo elemento.
bidirectional_iterator_tag - I bidirectional iterator sono progettati per accedere agli elementi di un contenitore, navigando in entrambe le direzioni.
random_access_iterator_tag - Con tali Iterator si può accedere a qualsiasi elemento del contenitore.
Il parametro T - Tipo degli elementi a cui punta l'iterator.
#include <iostream> | |
#include <iterator> | |
using namespace std; | |
1 | class iteratore : public iterator<input_iterator_tag, int> { |
2 | int* p; |
3 | public: |
4 | iteratore(int* x) :p(x) {} |
5 | iteratore(const iteratore& mit) : p(mit.p) {} |
6 | iteratore& operator++() { |
++p; | |
return *this; | |
} | |
7 | iteratore operator++(int) { |
8 | iteratore tmp(*this); |
9 | operator++(); |
10 | return tmp; |
} | |
11 | bool operator==(const iteratore& rhs) { |
return p==rhs.p; | |
} | |
12 | bool operator!=(const iteratore& rhs) { |
return p!=rhs.p; | |
} | |
13 | int& operator*() { |
14 | return *p; |
} | |
}; | |
int main () { | |
15 | int numeri[]={10,20,30,40,50}; |
16 | iteratore inizio(numeri); |
17 | iteratore fine(numeri+5); |
18 | for (iteratore it(inizio); it!=fine; it++) |
19 | cout << *it << " "; |
cout << endl; | |
system("PAUSE"); | |
return 0; | |
} |
Commenti
1 | la classe iteratore viene derivata dalla classe base iterator, passando i parametri alla classe base, cioè: Categoria: Input, Tipo: int |
2 | la classe possiede il campo membro p di tipo puntatore a intero. |
3 | inizia la sezione pubblica, cioè l'interfaccia della classe. |
4 | il costruttore riceve un puntatore a intero. Dopo l'elenco dei parametri del costruttore c'è un carattere due punti. Questo precede l'elenco dei costruttori della classe base. In questo caso si deve inizializzare il campo membro p della classe derivata. L'inizializzazione avviene in modo implicito: p(x). Cioè per inzializzare p si usa la stessa sintassi di un costruttore. è equivalente a scrivere il costruttore in questo modo: iteratore(int* x) { p = x; } |
5 | viene definito il costruttore copia (per il caso in cui il parametro di ingresso è il riferimento ad un altro oggetto di classe iteratore). Il campo membro viene aggiornato per assumere il valore deil campo membro dell'oggetto ricevuto come parametro. |
6 | viene ridefinito l'operatore di incremento: incrementa il puntatore e ritorna il riferimento all'oggetto che ha eseguito l'incremento. |
7 | viene ridefinito l'operatore di incremento che agisce su un operando di tipo intero: |
8 | viene prima creato un oggetto temporaneo, passando al costruttore il riferimento all'oggetto |
9, | chiama l'operatore di incremento precedentemente ridefinito restituisce l'oggetto che ha creato. |
10 | Restituisce l'oggetto che ha creato |
11 | ridefinisce l'operatore di confronto |
12 | ridefinisce l'operatore di confronto |
13 | ridefinisce l'operatore * |
15 | dichiara ed inizializza un array di interi. |
16 | crea l'istanza inizio di un oggetto di classe iteratore, inizializzando il campo membro con il riferimento al primo elemento dell'array. |
17 | crea l'istanza fine di un oggetto di classe iteratore, inizializzando il campo membro con il riferimento al 5o elemento dell'array. |
18 | il ciclo for crea l'oggetto temporaneo it, facendolo variare da inizio a fine, con incremento. è qui che vengono richiamati gli operatori riderifiniti: costruttore con parametro, confronto, incremento. |
19 | la stampa richiama l'operatore ridefinito *. |
#include <iostream> #include <vector> using namespace std; int main () { unsigned int i;
Sono consentiti quattro costruttori:
costruttore empty (costruttore di default): riserva lo spazio per un vettore vuoto.
vector<int> primo; // primo è un un vettore di interi vuoto
costruttore fill: riserva lo spazio per n elementi, tutti con lo stesso valore.
vector<int> secondo(4,100); // secondo è un vettore riempito con 4 interi di valore 100
costruttore range: riserva lo spazio per contenere gli elementi compresi in un intervallo
vector<int> terzo (secondo.begin(),secondo.end()); // itera attraverso secondo
costruttore copia: riserva lo spazio per contenere gli elementi di un altro oggetto.
vector<int> quarto(terzo); // copia di terzo
// Il costruttore con iteratore può costruire un vector anche a partire da un array:
int interi[] = {16, 2, 77, 29}; vector<int> quinto(interi, interi + sizeof(interi)/sizeof(int) ); cout << "Quinto contiene: "; for (vector<int>::iterator it=quinto.begin(); it!=quinto.end(); ++it) cout << " " << *it; cout << endl; return 0; }
vector::assign
Assegna nuovi valori al vettore, sostituendo gli elementi già contenuti e modificando la dimensione. Esiste in due versioni:
int main () { vector<int< primo; vector<int> secondo; vector<int> terzo;
Nella versione fill il nuovo contenuto sarà formato da n elementi, ciascuno inizializzato con uno stesso valore.
primo.assign (7, 100); // 7 interi di valore 100
Nella versione che specifica un range gli elementi sono costruiti prelevandoli in un intervallo specificato.
vector<int>::iterator it; it=primo.begin()+1; secondo.assign(it, primo.end()-1); // I 5 valori centrali di primo
L'intervallo può anche riferirsi ad un array
int interi[] = {1776, 7, 4}; terzo.assign (interi, interi+3); // assegnazione da un array. return 0; }
vector::at
Restituisce un riferimento all'elemento in posizione n nel vettore. La funzione controlla se l'indice n si trova entro i limiti validi per l'array. In caso negativo si genera un errore.
int main () { vector<int> vettore(10); // 10 elementi inizializzati a zero for (unsigned i=0; i<vettore.size(); i++) vettore.at(i) = i; // assegnazione di nuovi valori: cout << "valori contenuti in vettore:"; for (unsigned i=0; i<vettore.size(); i++) cout << ' ' << vettore.at(i); cout << endl; return 0; }
vector::back
Restituisce un riferimento all'ultimo elemento del vettore. A differenza della funzione vector::end, che restituisce un iteratore all'elemento successivo all'ultimo, questa funzione restituisce un riferimento diretto. Ha un comportamento imprevedibile se viene richiamata su un vettore vuoto.
int main () { vector<int> vettore; vettore.push_back(10); while (vettore.back() != 0) { vettore.push_back ( vettore.back() -1 ); } cout << "Gli Elementi contenuti nel vettore sono:"; for (unsigned i=0; i<vettore.size() ; i++) cout << ' ' << vettore[i]; cout << endl; return 0; }
Questo programma stampa:
Gli Elementi contenuti nel vettore sono: 10 9 8 7 6 5 4 3 2 1 0
vector::begin
Restituisce un iterator che punta al primo elemento nel vettore.
int main () { vector<int> vettore; for (int i=1; i<=5; i++) vettore.push_back(i); cout << "vettore contiene:"; for (vector<int>::iterator it=vettore.begin() ; it!=vettore.end(); ++it) cout << ' ' << *it; cout << endl; return 0; }
Questo programma stampa:
vettore contiene: 1 2 3 4 5
vector::clear
Elimina tutti gli elementi dal vettore, che così diventa di dimensione 0.
vector::empty
Restituisce true se il vettore è vuoto. Restituisce un iteratore che fa riferimento alla posizione che segue l'ultimo elemento nel vettore. Siccome non esiste un elemento in tale posizione, non è ammesso accedere a quell'elemento.
Esempio:
int main () { vector<int> vettore; for (int i=1; i<=5; i++) vettore.push_back(i); cout << "vettore contiene:"; for(vector<int>::iterator it=vettore.begin(); it != vettore.end(); ++it) cout << ' ' << *it; cout << '\n'; return 0; }
il cui output è:
vettore contiene: 1 2 3 4 5
vector::erase
Esiste nella versione per rimuovere un singolo elemento o gli elementi compresi in un intervallo. La dimensione del vettore si riduce della quantità di elementi eliminati.
Restituisce un iteratore all'elemento che segue quello cancellato.
int main () { vector<int> vettore; for (int i=1; i<=10; i++) vettore.push_back(i); // inserisce i valori da 1 a 10 vettore.erase (vettore.begin()+5); // elimina il VI elemento vettore.erase (vettore.begin(), vettore.begin()+3); // elimina i primi 3 elementi cout << "vettore contiene:"; for (unsigned i=0; i<vettore.size(); ++i) cout << ' ' << vettore[i]; cout << '\n'; return 0; }
L'output del programma è:
vettore contiene: 4 5 7 8 9 10
vector::front
Restituisce un riferimento al primo elemento del vettore.
int main () { vector<int> vettore; vettore.push_back(78); vettore.push_back(16); // front contiene 78, e back contiene 16 vettore.front() -= vettore.back(); cout << "vettore.front() = " << vettore.front() << '\n'; return 0; }
L'output del programma è:
vettore.front() = 62
vector::insert
Inserisce elementi nel vettore. La dimensione del vettore aumenta della quantità di elementi inseriti prima della posizione specificata. Restituisce un iteratore che punta al primo degli elementi inseriti.
int main () { // costruttore tipo fill 1 vector<int> vettore(3,100); // riferimento a un vettore di interi 2 vector<int>::iterator it; // un riferimento al primo elemento 3 it = vettore.begin(); 4 it=vettore.insert(it,200); 5 vettore.insert(it, 2, 300); // riottiene un riferimento al primo elemento 6 it = vettore.begin(); 7 vector<int> vettore2 (2,400); 8 vettore.insert (it+2, vettore2.begin(), vettore2.end()); 9 int array [] = { 501,502,503 }; // inserisce gli elementi di array 10 vettore.insert (vettore.begin(), array, array+3); cout << "vettore contiene:"; // Stampa tuti gli elementi di "vettore": for (it=vettore.begin(); it<vettore.end(); it++) cout << ' ' << *it; return 0; }
1: Il costruttore, richiamato secondo la sintassi 'fill', crea il vettore e inserisce 3 valori uguali.
vettore: | 100 | 100 | 100 |
Crea un riferimento ad un vettore di interi.
Assegna al riferimento l'indirizzo del primo elemento del vettore di interi.
inserisce un elemento (200) prima della posizione it
prima di inserire | it↓ | |||
vettore: | 200 | 100 | 100 | 100 |
dopo l'inserimento: | it↑ |
prima della posizione it inserisce 2 elementi (300)
vettore: | 300 | 300 | 200 | 100 | 100 | 100 |
Riporta il puntatore al primo elemento del vettore.
crea un altro vettore con 2 elementi di valore 400
prima del terzo elemento di vettore inserisce gli elementi del secondo vettore:
prima di inserire: | it ↓ | it +1 ↓ | it+2 ↓ | |||||
vettore: | 300 | 300 | 200 | 100 | 100 | 100 | ||
dopo l'inserimento: | 300 | 300 | 400 | 400 | 200 | 100 | 100 | 100 |
Crea un array di interi
Inserisce gli elementi dell'array nel vettore.
Vettore: 501 502 503 300 300 400 400 200 100 100 100Vettore: | 501 | 502 | 503 | 300 | 300 | 400 | 400 | 200 | 100 | 100 | 100 |
vector::operator=
Assegna al vettore specificato a I membro dell'assegnazione i valori contenuti nel vettore a II membro, modificando, di conseguenza, la dimensione del vettore.
vector::operator[]
Ritorna un riferimento all'elemento che occupa la posizione specificata tra le parentesi quadre.
vector::pop_back
Rimuove, e distrugge, l'ultimo elemento del vettore.
vector::push_back
Aggiunge un nuovo elemento alla fine del vettore. La dimensione del vettore cresce e potrebbe essere necessario riposizionare il vettore in memoria.
int main () { vector<int> vettore; int numero; cout << "inserisci un numero (0 per finire):\n"; do { cin >> numero; vettore.push_back (numero); } while (numero); cout << " nel vettore ci sono " << int(vettore.size()) << " numeri.\n"; return 0; }