Tipi Personalizzati (Enum, Struct e Union)
I tipi fondamentali sono eccellenti per la matematica e la logica cruda, ma il vero cuore di un linguaggio moderno risiede nella capacità di definire Tipi Personalizzati (User-Defined Types), per modellare concetti legati al dominio del problema da risolvere.
Esistono tre grandi macro-categorie:
- Enumerazioni (
enum class): Limitano l’oggetto a un set predefinito di valori (es. i giorni della settimana). - Classi e Strutture (
struct/class): Raggruppano variabili di tipi diversi sotto un’unica entità concettuale. - Unioni (
union): Tipi a basso livello in cui tutti i membri condividono la stessa esatta cella di memoria.
1. Le Enumerazioni (Enum Classes)
Section titled “1. Le Enumerazioni (Enum Classes)”Le enumerazioni modellano concetti categorici. Impediscono di usare “numeri magici” senza senso, costringendo lo sviluppatore a usare etichette testuali esplicite e controllate dal compilatore.
Per dichiarare una enumerazione moderna e sicura (scoped enum), usa enum class:
enum class Razza { Umano, Elfo, Nano, Orco};Per assegnare o leggere uno di questi valori, dovrai anteporre il nome della classe e due punti :: (operatore di risoluzione di scope):
Razza nemico = Razza::Orco;Le istruzioni switch
Section titled “Le istruzioni switch”Gli enum class si sposano magnificamente con le istruzioni switch, che trasferiscono il controllo di esecuzione a uno di vari case in base al valore della condizione (molto più pulito di una sfilza infinita di if-else!).
Esercizio: Lo Switch e l’Enum
Section titled “Esercizio: Lo Switch e l’Enum”L’istruzione switch valuta la condizione e trasferisce l’esecuzione al case corrispondente. Attenzione però alla trappola fatale: l’omissione del break causa il fallthrough!
Completa il seguente codice inserendo il blocco case mancante per il Nano (Stampa “Difesa: 15\n”) e assicurati di posizionare correttamente i break;.
2. Le Classi “Plain-Old-Data” (POD Structs)
Section titled “2. Le Classi “Plain-Old-Data” (POD Structs)”Spesso abbiamo bisogno di raggruppare dati di tipo diverso in un unico concetto logico (es. un Libro ha un titolo, un anno e un numero di pagine). A questo scopo nascono i POD (Plain-Old-Data classes).
Sono semplici contenitori, eterogenei, definiti tramite la parola chiave struct. Ogni variabile dichiarata al loro interno è chiamata membro.
struct Libro { char nome[256]; int anno; int pagine; bool copertina_rigida;};Per creare una variabile di tipo Libro e accedere ai suoi campi interni si utilizza l’operatore punto (.):
Libro neuromante;neuromante.pagine = 271;printf("Pagine: %d", neuromante.pagine);Il C++ garantisce che in una struct POD i membri verranno salvati in modo sequenziale all’interno della memoria RAM, il che le rende utilissime per interagire ad alta efficienza con il vecchio codice C.
3. Le Unioni (Unions) - Da usare con cautela
Section titled “3. Le Unioni (Unions) - Da usare con cautela”L’union è la cugina a basso livello della struct. La sua particolarità è inquietante: sovrappone fisicamente tutti i suoi membri nello stesso identico spazio di memoria.
L’union occupa solo i byte necessari a contenere il suo membro più grande. Dichiariamola:
union Variante { char stringa[10]; int intero; double virgola_mobile;};Il disastro avviene quando interpreti in modo errato i byte conservati nella RAM. Guarda questo esempio (ispirato al Listing 2-18 del libro di riferimento):
Hai scritto il valore di Eulero, occupando quei byte come se fossero un double. Quando hai tentato di stamparli nuovamente come se fossero un int (che era 42 all’inizio), il compilatore ha letto alla cieca i bit frazionari di Eulero traducendoli in un intero totalmente casuale (-1961734133). Abbiamo sovrascritto, o clobberato, i dati!
Il compilatore non terrà traccia di quale versione è correntemente attiva. Spetta allo sviluppatore non commettere errori. A causa dell’altissimo rischio di corruzione dei dati, nel C++ moderno le unioni dirette si evitano quasi totalmente a favore di classi sicure della libreria standard (come std::variant).