Cosa fanno gli operatori
Un operatore è un simbolo che esegue un'azione su uno o più valori, chiamati operandi. Ne hai già usato uno: il = che lega un valore a una variabile. C++ offre un ricco insieme di operatori per matematica, confronti, logica e manipolazione di bit, e qualche spigolo vivo che mette in difficoltà chi inizia.
Nella pagina precedente hai visto come const blocca un valore. Gli operatori sono il modo in cui calcoli i valori che memorizzi, costanti o no. Vediamo le famiglie che userai ogni giorno.
Operatori aritmetici
I cinque operatori aritmetici sono +, -, *, / e % (modulo, il resto di una divisione):
La grande trappola si nasconde in quel quoziente. 17 / 5 stampa 3, non 3.4. Quando entrambi gli operandi sono interi, / esegue la divisione intera e butta via la parte frazionaria: tronca verso lo zero, non arrotonda. Se vuoi una vera frazione, almeno un operando deve essere in virgola mobile:
L'operatore % funziona solo con gli interi. Usare % su un double è un errore di compilazione: per il resto in virgola mobile usa std::fmod da <cmath>.
Assegnazione e operatori composti
Un singolo = assegna, non confronta. C++ ti offre forme composte che uniscono un'operazione e un'assegnazione, così non ripeti il nome della variabile:
Un errore classico e doloroso è scrivere = dove intendevi == dentro una condizione. if (x = 0) assegna 0 a x e poi valuta il risultato (che è falso), invece di confrontare. I compilatori moderni lo segnalano con un avviso quando attivi gli avvisi: tieni attivo -Wall e prendi sul serio l'avviso.
Operatori di confronto
Gli operatori di confronto pongono una domanda sì/no e producono un bool (true o false):
Per default cout stampa un bool come 1 o 0. Invia boolalpha allo stream una volta e passa alle parole true/false per il resto di quello stream.
Una trappola sottile: non concatenare confronti come 1 < x < 10. Viene interpretato come (1 < x) < 10: il primo confronto produce un bool (0 o 1), che viene poi confrontato con 10, quindi il tutto è quasi sempre true. Scrivi invece 1 < x && x < 10.
Operatori logici e cortocircuito
&& (e), || (o) e ! (non) combinano espressioni booleane. I primi due sono a cortocircuito: la valutazione si ferma non appena il risultato è noto.
Né check("A") né check("B") vengono mai eseguiti: questo è il cortocircuito. Non è solo un'ottimizzazione, è uno strumento. Puoi scrivere in sicurezza if (ptr != nullptr && ptr->ready) perché la parte ptr->ready viene raggiunta solo quando ptr non è nullo, evitando di dereferenziare un puntatore non valido.
Incremento e decremento
++ aggiunge uno; -- sottrae uno. Ognuno ha una forma prefissa e una postfissa, e la differenza conta quando usi il risultato:
Quando vuoi solo l'effetto collaterale (per esempio in un ciclo for), preferisci ++i. Per un semplice int è identico, ma per tipi più pesanti come gli iteratori il post-incremento deve prima copiare il vecchio valore, ed è lavoro sprecato.
Un altro avvertimento: non modificare la stessa variabile due volte in una sola espressione, come i = i++ + 1; o arr[i] = i++;. L'ordine di quegli aggiornamenti non è specificato e il risultato è undefined behavior. Limita ogni variabile a una sola modifica per istruzione.
Operatori bit a bit e precedenza
Per il lavoro a basso livello ci sono gli operatori bit a bit: & (and), | (or), ^ (xor), ~ (not) e gli shift << e >>.
Attenzione: << e >> sono anche gli operatori di stream di cout. Dentro una riga con cout di solito servono le parentesi attorno a uno shift di bit, altrimenti il compilatore lo legge come un inserimento nello stream.
Infine, la precedenza decide cosa si lega per primo quando mescoli gli operatori. * e / legano più forte di + e -, proprio come in matematica, quindi 2 + 3 * 4 vale 14. Il confronto lega meno dell'aritmetica, e i logici &&/|| ancora meno. Nel dubbio, non imparare a memoria l'intera tabella: aggiungi le parentesi. (a + b) * c è più chiaro che contare sul fatto che chi legge ricordi le regole.
Prossimo passo: la conversione di tipo
Hai visto sopra che (double)a / b ha forzato un intero in una divisione in virgola mobile. Quello è un cast: convertire deliberatamente un valore da un tipo a un altro. Nella prossima pagina vedremo gli strumenti di conversione di C++, dalle promozioni implicite a static_cast, e quando ciascuno è sicuro.
Domande frequenti
Quali sono gli operatori in C++?
C++ raggruppa gli operatori in aritmetici (+ - * / %), di confronto (== != < > <= >=), logici (&& || !), di assegnazione (= += -= ...), di incremento e decremento (++ --) e bit a bit (& | ^ ~ << >>). Ci sono anche il ternario ?: e pochi altri come sizeof.
Perché 5 / 2 fa 2 in C++?
Perché entrambi gli operandi sono int, quindi / esegue una divisione intera e la parte frazionaria viene scartata, non arrotondata. Per ottenere 2.5, rendi almeno un operando un valore in virgola mobile: 5.0 / 2 o 5 / 2.0.
Qual è la differenza tra ++i e i++ in C++?
Entrambi aggiungono 1 a i. ++i (pre-incremento) incrementa prima e restituisce il nuovo valore; i++ (post-incremento) restituisce il vecchio valore e poi incrementa. Quando ti interessa solo l'effetto collaterale, preferisci ++i.