Template e STL
Template di funzione e classe, Standard Template Library: container, algoritmi e smart pointer.
Template di Funzione
I template permettono di scrivere codice generico che funziona con qualsiasi tipo di dato. Il compilatore genera il codice specifico per ogni tipo usato.
#include <iostream>
#include <string>
using namespace std;
// Template di funzione: T è un tipo generico
template <typename T>
T massimo(T a, T b) {
return (a > b) ? a : b;
}
// Template con più tipi
template <typename T, typename U>
void stampa_coppia(T a, U b) {
cout << "(" << a << ", " << b << ")\n";
}
// Specializzazione esplicita per const char*
template <>
const char* massimo(const char *a, const char *b) {
return (strlen(a) > strlen(b)) ? a : b;
}
// Template con parametro non-tipo
template <int N>
void ripeti(const string &s) {
for (int i = 0; i < N; i++) cout << s << " ";
cout << "\n";
}
int main() {
cout << massimo(10, 20) << "\n"; // int
cout << massimo(3.14, 2.71) << "\n"; // double
cout << massimo('A', 'Z') << "\n"; // char
cout << massimo("ciao", "mondo!") << "\n"; // specializzato
stampa_coppia(42, "hello");
stampa_coppia(3.14, true);
ripeti<5>("Echo");
return 0;
}
Container STL
vector — Array dinamico
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {5, 2, 8, 1, 9, 3};
v.push_back(7); // Aggiunge in coda
v.insert(v.begin(), 0); // Inserisce in posizione
cout << "Size: " << v.size() << ", Capacity: " << v.capacity() << "\n";
// Range-based for (C++11)
for (int n : v) cout << n << " ";
cout << "\n";
// Algoritmi STL
sort(v.begin(), v.end()); // Ordina
cout << "Ordinato: ";
for (int n : v) cout << n << " ";
cout << "\n";
auto it = find(v.begin(), v.end(), 5); // Cerca
if (it != v.end())
cout << "5 trovato a posizione " << (it - v.begin()) << "\n";
// Lambda con find_if
auto pari = find_if(v.begin(), v.end(), [](int n){ return n % 2 == 0; });
cout << "Primo pari: " << *pari << "\n";
int somma = 0;
for_each(v.begin(), v.end(), [&somma](int n){ somma += n; });
cout << "Somma: " << somma << "\n";
v.erase(v.begin() + 2); // Rimuove l'elemento a indice 2
v.pop_back(); // Rimuove l'ultimo
return 0;
}
map e unordered_map — Dizionari
#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
using namespace std;
int main() {
// map: ordinato per chiave O(log n)
map<string, int> punteggi;
punteggi["Mario"] = 95;
punteggi["Giulia"] = 88;
punteggi["Luca"] = 72;
punteggi["Anna"] = 95;
// Iterazione ordinata per chiave
for (const auto &[nome, punteggio] : punteggi) {
cout << nome << ": " << punteggio << "\n";
}
// Cerca
if (punteggi.count("Mario"))
cout << "Mario: " << punteggi.at("Mario") << "\n";
// Rimuovi
punteggi.erase("Luca");
// unordered_map: non ordinato, O(1) medio con hash
unordered_map<string, int> frequenza;
string testo = "il gatto sul tetto il gatto mangia il topo";
istringstream ss(testo);
string parola;
while (ss >> parola) frequenza[parola]++;
cout << "\nFrequenza parole:\n";
for (const auto &[w, f] : frequenza)
cout << " " << w << ": " << f << "\n";
return 0;
}
set, queue, stack e altri
#include <iostream>
#include <set>
#include <queue>
#include <stack>
#include <utility> // pair
#include <tuple>
using namespace std;
int main() {
// set: elementi unici, ordinati
set<int> s = {5, 2, 8, 2, 1, 5, 9};
for (int n : s) cout << n << " "; // 1 2 5 8 9
cout << "\n";
s.insert(3);
cout << "3 nel set? " << (s.count(3) ? "Sì" : "No") << "\n";
// queue: FIFO
queue<string> coda;
coda.push("primo"); coda.push("secondo"); coda.push("terzo");
while (!coda.empty()) {
cout << coda.front() << " ";
coda.pop();
}
cout << "\n";
// stack: LIFO
stack<int> pila;
pila.push(1); pila.push(2); pila.push(3);
while (!pila.empty()) { cout << pila.top() << " "; pila.pop(); }
cout << "\n";
// pair
pair<string, int> p = {"ciao", 42};
cout << p.first << " " << p.second << "\n";
// tuple (C++11)
auto t = make_tuple("Mario", 25, 3.14);
cout << get<0>(t) << ", " << get<1>(t) << ", " << get<2>(t) << "\n";
return 0;
}
Algoritmi STL e Lambda
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric> // accumulate, reduce
#include <string>
using namespace std;
int main() {
vector<int> v = {3,1,4,1,5,9,2,6,5,3,5};
// sort con comparatore custom
sort(v.begin(), v.end(), [](int a, int b){ return a > b; }); // Decrescente
for (int n : v) cout << n << " ";
cout << "\n";
// unique: rimuove duplicati consecutivi (dopo sort)
sort(v.begin(), v.end());
auto ultimo = unique(v.begin(), v.end());
v.erase(ultimo, v.end());
cout << "Senza duplicati: ";
for (int n : v) cout << n << " ";
cout << "\n";
// transform: applica funzione a ogni elemento
vector<int> quadrati(v.size());
transform(v.begin(), v.end(), quadrati.begin(),
[](int n){ return n * n; });
cout << "Quadrati: ";
for (int n : quadrati) cout << n << " ";
cout << "\n";
// accumulate: somma (o operazione custom)
int somma = accumulate(v.begin(), v.end(), 0);
int prodotto = accumulate(v.begin(), v.end(), 1, [](int a, int b){ return a*b; });
cout << "Somma: " << somma << ", Prodotto: " << prodotto << "\n";
// count_if
int pari = count_if(v.begin(), v.end(), [](int n){ return n%2==0; });
cout << "Numeri pari: " << pari << "\n";
// any_of, all_of, none_of
bool tutti_pos = all_of(v.begin(), v.end(), [](int n){ return n>0; });
bool qualche_gt5 = any_of(v.begin(), v.end(), [](int n){ return n>5; });
cout << "Tutti > 0? " << tutti_pos << ", Qualcuno > 5? " << qualche_gt5 << "\n";
// min_element, max_element
auto minIt = min_element(v.begin(), v.end());
auto maxIt = max_element(v.begin(), v.end());
cout << "Min: " << *minIt << ", Max: " << *maxIt << "\n";
return 0;
}
Smart Pointer (C++11)
I smart pointer gestiscono automaticamente la memoria: chiamano delete quando non servono più, eliminando i memory leak.
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Risorsa {
string nome;
public:
Risorsa(const string &n) : nome(n) { cout << "Creato: " << nome << "\n"; }
~Risorsa() { cout << "Distrutto: " << nome << "\n"; }
void usa() const { cout << "Uso: " << nome << "\n"; }
};
int main() {
// unique_ptr: UNICO proprietario, non copiabile
{
auto ptr = make_unique<Risorsa>("unico");
ptr->usa();
// Automaticamente distrutto alla fine del blocco
}
cout << "Dopo il blocco\n\n";
// shared_ptr: proprietà CONDIVISA (reference counting)
shared_ptr<Risorsa> sp1 = make_shared<Risorsa>("condiviso");
cout << "Conteggio: " << sp1.use_count() << "\n"; // 1
{
shared_ptr<Risorsa> sp2 = sp1; // Copia: conteggio aumenta
cout << "Conteggio: " << sp1.use_count() << "\n"; // 2
sp2->usa();
}
// sp2 distrutto → conteggio torna a 1
cout << "Conteggio dopo blocco: " << sp1.use_count() << "\n"; // 1
// Uso tipico in vector
vector<unique_ptr<Risorsa>> risorse;
risorse.push_back(make_unique<Risorsa>("A"));
risorse.push_back(make_unique<Risorsa>("B"));
risorse.push_back(make_unique<Risorsa>("C"));
for (const auto &r : risorse) r->usa();
// Tutti automaticamente distrutti alla fine del main
return 0;
}
Leggi una stringa e usa map<char, int> per contare la frequenza di ogni carattere (ignorando maiuscole/minuscole). Ordina e stampa i caratteri per frequenza decrescente usando un vector<pair<char,int>>.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <cctype>
using namespace std;
int main() {
string testo;
cout << "Inserisci testo: ";
getline(cin, testo);
map<char, int> freq;
for (char c : testo) {
if (isalpha(c)) freq[tolower(c)]++;
}
vector<pair<char,int>> v(freq.begin(), freq.end());
sort(v.begin(), v.end(), [](const auto &a, const auto &b){
return a.second > b.second; // Ordine decrescente
});
for (const auto &[c, f] : v) {
cout << c << ": " << string(f, '#') << " (" << f << ")\n";
}
}Implementa una classe template Coppia<T, U> simile a std::pair con: costruttore, getter, swap() (scambia i valori), operator==, operator<<. Poi crea una funzione template zip(v1, v2) che unisca due vettori in un vettore di coppie.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <typename T, typename U>
class Coppia {
T primo;
U secondo;
public:
Coppia(T p, U s) : primo(p), secondo(s) {}
T getFirst() const { return primo; }
U getSecond() const { return secondo; }
void swap() { T tmp = primo; primo = secondo; secondo = tmp; } // solo se T==U
bool operator==(const Coppia &c) const {
return primo == c.primo && secondo == c.secondo;
}
friend ostream& operator<<(ostream &os, const Coppia &c) {
return os << "(" << c.primo << ", " << c.secondo << ")";
}
};
template <typename T, typename U>
vector<Coppia<T,U>> zip(const vector<T> &v1, const vector<U> &v2) {
vector<Coppia<T,U>> result;
int n = min(v1.size(), v2.size());
for (int i = 0; i < n; i++)
result.push_back({v1[i], v2[i]});
return result;
}
int main() {
Coppia<string, int> c1("ciao", 42);
Coppia<string, int> c2("mondo", 7);
cout << c1 << "\n" << c2 << "\n";
cout << "Uguali? " << (c1 == c2 ? "Sì" : "No") << "\n";
vector<string> nomi = {"Mario", "Giulia", "Luca"};
vector<int> voti = {28, 30, 25};
auto zipped = zip(nomi, voti);
for (const auto &c : zipped) cout << c << "\n";
}Implementa un grafo non orientato usando unordered_map<int, vector<int>>. Implementa: aggiungi_nodo, aggiungi_arco, bfs(start) (visita in ampiezza con queue), dfs(start) (visita in profondità con stack), percorso_minimo(src, dst) (BFS + ricostruzione del cammino).
#include <iostream>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
using namespace std;
class Grafo {
unordered_map<int, vector<int>> adj;
public:
void aggiungi_arco(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void bfs(int start) {
set<int> visitati;
queue<int> q;
q.push(start);
visitati.insert(start);
cout << "BFS: ";
while (!q.empty()) {
int n = q.front(); q.pop();
cout << n << " ";
for (int vic : adj[n]) {
if (!visitati.count(vic)) {
visitati.insert(vic);
q.push(vic);
}
}
}
cout << "\n";
}
void dfs(int start) {
set<int> visitati;
stack<int> s;
s.push(start);
cout << "DFS: ";
while (!s.empty()) {
int n = s.top(); s.pop();
if (visitati.count(n)) continue;
visitati.insert(n);
cout << n << " ";
for (int vic : adj[n]) {
if (!visitati.count(vic)) s.push(vic);
}
}
cout << "\n";
}
vector<int> percorso(int src, int dst) {
unordered_map<int,int> parent;
set<int> visitati;
queue<int> q;
q.push(src); visitati.insert(src); parent[src] = -1;
while (!q.empty()) {
int n = q.front(); q.pop();
if (n == dst) {
vector<int> path;
for (int x = dst; x != -1; x = parent[x]) path.push_back(x);
reverse(path.begin(), path.end());
return path;
}
for (int vic : adj[n]) {
if (!visitati.count(vic)) {
visitati.insert(vic);
parent[vic] = n;
q.push(vic);
}
}
}
return {};
}
};
int main() {
Grafo g;
g.aggiungi_arco(1, 2); g.aggiungi_arco(1, 3);
g.aggiungi_arco(2, 4); g.aggiungi_arco(3, 5);
g.aggiungi_arco(4, 6); g.aggiungi_arco(5, 6);
g.bfs(1);
g.dfs(1);
auto path = g.percorso(1, 6);
cout << "Percorso 1→6: ";
for (int n : path) cout << n << " ";
cout << "\n";
}Crea una classe Pipeline<T> che permetta di concatenare trasformazioni su un vettore: filter(pred), map(func), reduce(func, init), take(n). Deve supportare il metodo chaining: Pipeline(v).filter(...).map(...).reduce(...). Esempio: prendi i numeri 1..20, filtra i pari, raddoppiali, somma i primi 5.
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>
using namespace std;
template <typename T>
class Pipeline {
vector<T> dati;
public:
Pipeline(vector<T> v) : dati(move(v)) {}
Pipeline<T> filter(function<bool(T)> pred) {
vector<T> res;
copy_if(dati.begin(), dati.end(), back_inserter(res), pred);
return Pipeline<T>(res);
}
template <typename U>
Pipeline<U> map(function<U(T)> f) {
vector<U> res;
transform(dati.begin(), dati.end(), back_inserter(res), f);
return Pipeline<U>(res);
}
Pipeline<T> take(int n) {
int sz = min((int)dati.size(), n);
return Pipeline<T>(vector<T>(dati.begin(), dati.begin() + sz));
}
T reduce(function<T(T,T)> f, T init) {
return accumulate(dati.begin(), dati.end(), init, f);
}
void print() const {
cout << "[ ";
for (const auto &x : dati) cout << x << " ";
cout << "]\n";
}
vector<T> toVector() const { return dati; }
};
int main() {
// Genera 1..20
vector<int> numeri(20);
iota(numeri.begin(), numeri.end(), 1);
// Pipeline: filtra pari → raddoppia → prendi i primi 5 → somma
int risultato = Pipeline<int>(numeri)
.filter([](int n){ return n % 2 == 0; })
.map<int>([](int n){ return n * 2; })
.take(5)
.reduce([](int a, int b){ return a + b; }, 0);
cout << "Risultato: " << risultato << "\n";
// Visualizza ogni passo
Pipeline<int>(numeri)
.filter([](int n){ return n % 2 == 0; })
.map<int>([](int n){ return n * 2; })
.take(5)
.print();
}Guida Completata!
Hai percorso tutta la guida da Hello World fino alle STL avanzate di C++. Continua a praticare con esercizi su codeforces.com, leetcode.com o codeabbey.com.
Torna all'indice →