Modulo 20 — C++ Avanzato
Namespace in C++
Organizzare il codice, evitare conflitti di nomi, ADL (Argument-Dependent Lookup) e best practice.
Cos'è un Namespace?
Un namespace è un contenitore che raggruppa identificatori (funzioni, classi, variabili) per evitare conflitti di nomi. È come una "cartella" per il tuo codice.
C++Namespace — definizione e uso
#include <iostream>
using namespace std;
// Dichiarazione namespace
namespace geometria {
const double PI = 3.14159265358979;
double area_cerchio(double r) { return PI * r * r; }
double perimetro_cerchio(double r) { return 2 * PI * r; }
struct Punto {
double x, y;
double distanza(const Punto &altro) const {
double dx=x-altro.x, dy=y-altro.y;
return sqrt(dx*dx+dy*dy);
}
};
// Namespace annidato
namespace forme3d {
double volume_sfera(double r) { return (4.0/3.0) * PI * r * r * r; }
double volume_cilindro(double r, double h) { return PI * r * r * h; }
}
}
// I namespace possono essere "aperti" più volte
namespace geometria {
double area_triangolo(double b, double h) { return 0.5 * b * h; }
}
int main() {
// Accesso qualificato
cout << geometria::area_cerchio(5.0) << "\n";
cout << geometria::PI << "\n";
// Accesso namespace annidato
cout << geometria::forme3d::volume_sfera(3.0) << "\n";
// using declaration: porta UNO specifico nome
using geometria::Punto;
Punto p1{0, 0}, p2{3, 4};
cout << "Distanza: " << p1.distanza(p2) << "\n";
// using directive: porta TUTTO il namespace (sconsigliato nei header!)
// using namespace geometria;
// area_cerchio(5.0); // OK dopo using namespace
// Alias di namespace
namespace geo3d = geometria::forme3d;
cout << "Volume cilindro: " << geo3d::volume_cilindro(2, 5) << "\n";
return 0;
}
Anonymous Namespace e Inline Namespace
C++Namespace anonimo e inline namespace
#include <iostream>
using namespace std;
// Namespace anonimo: visibile SOLO in questo file
// Equivale a "static" in C — evita conflitti tra file diversi
namespace {
int helper_privato(int x) { return x * x; }
const int COSTANTE_LOCALE = 42;
}
// Inline namespace: per versioning di API
namespace libreria {
inline namespace v2 { // Versione corrente (default)
void funzione() { cout << "libreria::v2::funzione\n"; }
int nuova_api() { return 42; }
}
namespace v1 { // Versione vecchia (accesso esplicito)
void funzione() { cout << "libreria::v1::funzione (obsoleta)\n"; }
}
}
int main() {
// Il namespace anonimo è accessibile nel file corrente
cout << helper_privato(5) << "\n"; // 25
cout << COSTANTE_LOCALE << "\n"; // 42
// Inline namespace: accesso senza qualifica (usa v2)
libreria::funzione(); // chiama v2
libreria::v1::funzione(); // chiama v1
libreria::v2::funzione(); // chiama v2 esplicito
cout << libreria::nuova_api() << "\n"; // 42
return 0;
}
ADL — Argument-Dependent Lookup
L'ADL (o Koenig lookup) è una regola del C++ per cui il compilatore cerca automaticamente le funzioni anche nei namespace degli argomenti. Permette di scrivere cout << x invece di std::operator<<(cout, x).
C++ADL in pratica
#include <iostream>
using namespace std;
namespace fisica {
struct Vettore { double x, y, z; };
// Operator definito nello stesso namespace di Vettore
Vettore operator+(const Vettore &a, const Vettore &b) {
return {a.x+b.x, a.y+b.y, a.z+b.z};
}
ostream &operator<<(ostream &os, const Vettore &v) {
return os << "(" << v.x << "," << v.y << "," << v.z << ")";
}
double norma(const Vettore &v) {
return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}
}
// swap personalizzato — ADL permette a std::sort, std::swap etc.
// di trovarlo automaticamente
namespace mylib {
struct Buffer {
int *dati; int n;
Buffer(int n) : dati(new int[n]()), n(n) {}
~Buffer() { delete[] dati; }
Buffer(Buffer &&o) noexcept : dati(o.dati), n(o.n) { o.dati=nullptr; }
};
// swap trovato da ADL quando si usa std::sort su mylib::Buffer
void swap(Buffer &a, Buffer &b) noexcept {
using std::swap;
swap(a.dati, b.dati);
swap(a.n, b.n);
}
}
int main() {
fisica::Vettore v1{1,2,3}, v2{4,5,6};
// ADL: il compilatore cerca operator+ in fisica:: (namespace di v1, v2)
fisica::Vettore v3 = v1 + v2; // Trova fisica::operator+
// ADL: il compilatore cerca operator<< in fisica:: (namespace di v3)
cout << v3 << "\n"; // Trova fisica::operator<<
// ADL: cerca norma in fisica::
cout << "norma: " << norma(v1) << "\n"; // Trova senza qualifica!
return 0;
}
Best Practice
C++Cosa fare e cosa NON fare
// ✅ CORRETTO: usa using declaration per nomi specifici
using std::cout;
using std::string;
using std::vector;
// ✅ CORRETTO: using namespace in .cpp (mai in header)
// In file.cpp — OK
using namespace std;
// ❌ SBAGLIATO: using namespace std nei file header
// — contamina chiunque includa il tuo header
// #include "mio_header.h" // Portato using namespace std!
// ✅ CORRETTO: struttura namespace per progetto grande
namespace myapp {
namespace core {
class Engine { /* ... */ };
}
namespace ui {
class Window { /* ... */ };
}
namespace utils {
void log(const std::string &msg);
}
}
// ✅ CORRETTO: swap idiom
namespace mylib {
class Widget {
std::string nome;
int val;
public:
Widget(std::string n, int v) : nome(n), val(v) {}
// Membro swap
void swap(Widget &other) noexcept {
std::swap(nome, other.nome);
std::swap(val, other.val);
}
// Operatore di assegnamento con copy-and-swap idiom
Widget &operator=(Widget other) noexcept {
swap(other);
return *this;
}
};
// Free function swap (trovata da ADL)
inline void swap(Widget &a, Widget &b) noexcept { a.swap(b); }
}
int main() {
using namespace myapp;
mylib::Widget w1{"alpha", 1}, w2{"beta", 2};
using std::swap;
swap(w1, w2); // ADL trova mylib::swap
return 0;
}
🏋️ Esercizi
1
Crea un namespace math con sotto-namespace algebra, stat e trig. In algebra: operazioni su matrici 2x2. In stat: media, varianza, deviazione standard. In trig: sin, cos, tan in gradi. Usali nel main senza using namespace.
C++
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
namespace math {
const double PI = 3.14159265358979;
namespace algebra {
struct Mat2 { double a,b,c,d; };
Mat2 mul(const Mat2 &x, const Mat2 &y) {
return {x.a*y.a+x.b*y.c, x.a*y.b+x.b*y.d,
x.c*y.a+x.d*y.c, x.c*y.b+x.d*y.d};
}
double det(const Mat2 &m) { return m.a*m.d - m.b*m.c; }
}
namespace stat {
double media(const std::vector<double> &v) {
return std::accumulate(v.begin(),v.end(),0.0) / v.size();
}
double varianza(const std::vector<double> &v) {
double m=media(v), s=0;
for(double x:v) s+=(x-m)*(x-m);
return s/v.size();
}
double dev_std(const std::vector<double> &v) { return std::sqrt(varianza(v)); }
}
namespace trig {
double sin_deg(double deg) { return std::sin(deg*PI/180); }
double cos_deg(double deg) { return std::cos(deg*PI/180); }
double tan_deg(double deg) { return std::tan(deg*PI/180); }
}
}
int main() {
using std::cout;
math::algebra::Mat2 A{1,2,3,4}, B{5,6,7,8};
auto C = math::algebra::mul(A,B);
cout << "A*B = [[" << C.a << "," << C.b << "],[" << C.c << "," << C.d << "]]\n";
cout << "det(A) = " << math::algebra::det(A) << "\n";
std::vector<double> dati = {2,4,4,4,5,5,7,9};
cout << "Media: " << math::stat::media(dati) << "\n";
cout << "Dev std: " << math::stat::dev_std(dati) << "\n";
cout << "sin(30°) = " << math::trig::sin_deg(30) << "\n"; // 0.5
cout << "cos(60°) = " << math::trig::cos_deg(60) << "\n"; // 0.5
return 0;
}