Operatori in C e C++
Questa è una lista degli operatori nei linguaggi di programmazione C e C++. Tutti gli operatori seguenti sono implementabili in quest'ultimo linguaggio, mentre invece altri non lo sono in C, come nel caso degli operatori di conversione di tipo (casting), ossia const_cast
, static_cast
, dynamic_cast
, e reinterpret_cast
[1][2][3].
Molti degli operatori disponibili in C e C++ sono implementabili pure in altri linguaggi della cosiddetta “famiglia C”, quali C#, D, ma anche Java, Perl e PHP, mantenendo le medesime caratteristiche mostrate (arietà, posizione e associatività)[4][5].
Sintassi degli operatori
Nelle tabelle seguenti, a
, b
e c
rappresentano valori validi qualsiasi (literals, valori da variabili, oppure return value) o, in alcuni casi specifici, nomi di oggetti o lvalues. R
, S
e T
indicano qualsiasi tipo, mentre K
indica un tipo di classe o un tipo enum[6][7].
Operatori aritmetici
Tutti gli operatori aritmetici esistono sia in C e C++ e possono essere, come già indicato, sovraccaricati solo in C++[8].
Nome operatore | Sintassi | Esempi di implementazione in C++ | ||
---|---|---|---|---|
Come membro della classe K | Fuori dalla classe | |||
Addizione | a+b
|
R K::operator +(S b);
|
R operator +(K a, S b);
| |
Sottrazione | a-b
|
R K::operator -(S b);
|
R operator -(K a, S b);
| |
Unario più | +a
|
R K::operator +(); | R operator +(K a); | |
Unario meno | -a | R K::operator -(); | R operator -(K a); | |
Moltiplicazione | a * b
|
R K::operator *(S b);
|
R operator *(K a, S b);
| |
Divisione | a / b
|
R K::operator /(S b);
|
R operator /(K a, S b);
| |
Modulo | a % b
|
R K::operator %(S b);
|
R operator %(K a, S b);
| |
Incremento | Prefisso | ++a
|
R& K::operator ++();
|
R& operator ++(K& a);
|
Postfisso | a++
|
R K::operator ++(int);
|
R operator ++(K& a, int);
| |
Il C++ utilizza il parametro fittizio senza nome int per differenziare gli operatori di incremento prefisso e postfisso. | ||||
Decremento | Prefisso | --a
|
R& K::operator --();
|
R& operator --(K& a);
|
Postfisso | a--
|
R K::operator --(int);
|
R operator --(K& a, int);
| |
Il C++ utilizza il parametro fittizio senza nome int per differenziare gli operatori di incremento prefisso e postfisso. |
Operatori relazionali
Tutti gli operatori di confronto possono essere sovraccaricati solo in C++. Dal C++20, l'operatore di disuguaglianza viene generato automaticamente se viene definito operator==
e tutti e quattro gli operatori relazionali vengono generati automaticamente se viene definito operator<=>
.
Nome operatore | Sintassi | Incluso in C | Esempi di implementazione in C++ | |
---|---|---|---|---|
Come membro della classe K | Fuori dalla classe | |||
Uguale | a = b
|
Sì | bool K::operator ==(S const& b) const;
|
bool operator ==(K const& a, S const& b);
|
Diverso | a != b |
Sì | bool K::operator !=(S const& b) const;
|
bool operator !=(K const& a, S const& b);
|
Maggiore di | a > b |
Sì | bool K::operator >(S const& b) const;
|
bool operator >(K const& a, S const& b);
|
Minore di | a < b |
Sì | bool K::operator <(S const& b) const;
|
bool operator <(K const& a, S const& b);
|
Maggiore o uguale a | a >= b |
Sì | bool K::operator >=(S const& b) const;
|
bool operator >=(K const& a, S const& b);
|
Minore o uguale a | a <= b |
Sì | bool K::operator <=(S const& b) const;
|
bool operator <=(K const& a, S const& b);
|
Operatori logici (o booleani)
Tutti gli operatori logici (o booleani[9]) sono esistenti sia in C che in C++ e possono essere chiaramente sovraccaricati solo in quest'ultimo linguaggio di programmazione.
Nonostante la possibilità di sovraccarico in C++, tale operazione è sconsigliata con AND logico e OR logico sia sconsigliato perché, come operatori sovraccaricati, si comporterebbero come normali chiamate di funzione, il che significa che entrambi i loro operandi verrebbero valutati, perdendo di conseguenza la loro importante valutazione di McCarthy. [10]
Nome dell'operatore | Sintassi | Esempi di implementazione in C++ | |
---|---|---|---|
Come membro della classe K | Fuori dalla classe | ||
NOT logico | ! a
|
bool K::operator !();
|
bool operator !(K a);
|
AND logico | a && b
|
bool K::operator &&(S b);
|
bool operator &&(K a, S b);
|
OR logico | a || b
|
bool K::operator ||(S b);
|
bool operator ||(K a, S b);
|
Operatori bit a bit
Tutti gli operatori bit a bit (o di manipolazione dei bit) esistono sia C che C++ e possono essere sovraccaricati soltanto nel linguaggio inventato da Bjarne Stroustrup[11].
Nome dell'operatore | Sintassi | Esempi di implementazione in C++ | |
---|---|---|---|
Come membro della classe K | Fuori dalla classe | ||
Complemento a uno (inversione di tutti i bit) | ~a
|
R K::operator ~();
|
R operator ~(K a);
|
AND bit a bit | a & b
|
R K::operator &(S b);
|
R operator &(K a, S b);
|
OR inclusivo bit a bit | a | b
|
R K::operator |(S b);
|
R operator |(K a, S b);
|
OR esclusivo bit a bit (OR exclusive, XOR) | a ^ b
|
R K::operator ^(S b);
|
R operator ^(K a, S b);
|
Spostamento di tutti i bit verso sinistra | a << b
|
R K::operator <<(S b);
|
R operator <<(K a, S b);
|
Spostamento di tutti i bit verso destra | a >> b
|
R K::operator >>(S b);
|
R operator >>(K a, S b);
|
Operatori ed espressioni di assegnamento
Tutti gli operatori e le espressioni di assegnamento sono esistenti sia in C che in C++ e possono essere chiaramente sovraccaricate solo in quest'ultimo linguaggio di programmazione.
Per gli operatori indicati, la semantica dell'espressione di assegnazione combinata incorporata a ⊚= b
è equivalente a a = a ⊚ b
, eccetto per il fatto che a
viene valutata una sola volta.
Tipologia assegnamento | Sintassi | Esempi di implementazione in C++ | |
---|---|---|---|
Come membro della classe K | Fuori dalla classe | ||
Espressione semplice di assegnamento | a = b
|
R& K::operator =(S b);
|
N.D. |
Assegnamenti composti | a += b
|
R& K::operator +=(S b);
|
R& operator +=(K& a, S b);
|
a -= b
|
R& K::operator -=(S b);
|
R& operator -=(K& a, S b);
| |
a *= b
|
R& K::operator *=(S b);
|
R& operator *=(K& a, S b);
| |
a /= b
|
R& K::operator /=(S b);
|
R& operator /=(K& a, S b);
| |
a %= b
|
R& K::operator %=(S b);
|
R& operator %=(K& a, S b);
| |
a &= b
|
R& K::operator &=(S b);
|
R& operator &=(K& a, S b);
| |
a |= b
|
R& K::operator |=(S b);
|
R& operator |=(K& a, S b);
| |
a ^= b
|
R& K::operator ^=(S b);
|
R& operator ^=(K& a, S b);
| |
a <<= b
|
R& K::operator <<=(S b);
|
R& operator <<=(K& a, S b);
| |
a >>= b
|
R& K::operator >>=(S b);
|
R& operator >>=(K& a, S b);
|
Operatori membro e puntatore
Nome operatore[12] | Sintassi | Ammette overload (sovraccarico) in C++ | Implementabile in C | Esempi di implementazione in C++ | |
---|---|---|---|---|---|
Come membro della classe K | Fuori dalla classe | ||||
Indicizzazione | a[b]
|
Sì | Sì | R& K::operator [](S b);
|
N.D. |
Deferenziazione | *a
|
Sì | Sì | R& K::operator *(); | R& operator *(K a); |
Indirizzo | &a
|
Sì | Sì | R* K::operator &(); | R* operator &(K a); |
Deferenziazione e selezione | a->b
|
Sì | Sì | R* K::operator ->(); | N.D. |
Selezione (object.member) | a.b
|
No | Sì | N.D. | N.D. |
Deferenziazione e selezione con puntatore a membro | a->*b
|
Sì | No | R& K::operator ->*(S b); | R& operator ->*(K a, S b); |
Selezione con puntatore a membro | a.*b
|
No | No | N.D. | N.D. |
Altri operatori
Nome operatore | Sintassi | Ammette overload (sovraccarico) in C++ | Implementabile in C | Esempi di implementazione in C++ | |
---|---|---|---|---|---|
Come membro della classe K | Fuori dalla classe | ||||
Chiamata di funzione | a(a1, a2)
|
Sì | Sì | R K::operator ()(S a, T b, ...); | |
Virgola | a, b
|
Sì | Sì | R K::operator ,(S b);
|
R operator ,(K a, S b);
|
Espressione condizionale | a ? b : c
|
No | Sì | N.D. | N.D. |
Risolutore di visibilità | a::b
|
No | No | N.D. | N.D. |
Dimensione di oggetto o di tipo (sizeof) | sizeof a
|
No | Sì | N.D. | N.D. |
Conversione di tipo static_cast | static_cast<R>(a)
|
Sì | No | K::operator R();
|
N.D. |
Conversione const const_cast | const_cast<R>(a)
|
No | No | N.D. | N.D. |
Allocazione | new R
|
Sì | No | void* K::operator new(size_t x);
|
void* operator new(size_t x);
|
Allocazione di array | new R[n]
|
Sì | No | void* K::operator new[](size_t a);
|
void* operator new[](size_t a);
|
Deallocazione | delete a
|
Sì | No | void K::operator delete(void* a);
|
void operator delete(void* a);
|
Deallocazione di array | delete[] a
|
Sì | No | void K::operator delete[](void* a);
|
void operator delete[](void* a);
|
Overloading degli operatori
C++ possiede molti operatori già predefiniti in grado di operare su vari tipi di dato, perciò si può parlare di “operatori già sovraccaricati”. Per esempio l’operatore +
può essere implementato per sommare sia due variabili di tipo int
(interi), sia due variabili di tipo float
o double
(variabili a virgola mobile).
Oltre a quando detto in precedenza, il programmatore può anche “estendere” l’operatività di ciascuno di essi; per esempio operandi complessi definiti con le classi, si potrebbero trattare anche come se fossero dati semplici[1].
In C++ è possibile sovraccaricare tutti gli operatori predefiniti, con l’eccezione su accesso al membro (.)
, indirezione di accesso al membro (.*)
e risoluzione di ambito (::)
.
Nel caso degli operatori, il numero degli operandi e la priorità dell’operatore sovraccaricato sono le stesse dell’operatore predefinito, mentre il nuovo operatore può essere definito come una funzione membro oppure come una funzione friend (funzione amica)[14].
Proprietà degli operatori
Ogni operatore possiede delle proprietà, in modo tale da permettere l'interpretazione univoca del significato di ogni singola espressione. Gli esempi che seguono sono tutti applicabili al linguaggio C++ e, a seconda dei casi, anche al C[15].
Posizione
Un operatore può precedere gli operandi (o argomenti), seguirli oppure né precederli né seguirli[12]. In questi casi parleremo rispettivamente di operatori prefissi, postfissi oppure infissi.
Numero di operandi
Ogni operatore può avere un numero diverso di operandi (arietà).
a[b]
Nel caso riportato, l'operatore di indicizzazione possiede due operandi, ossia le parentesi quadre. L'arietà può essere anche di tre operandi, come nel caso degli operatori condizionali, riportato di seguito:
e1 ? e2 : e3
Precedenza degli operatori
Ogni operatore possiede una propria precedenza[16][17], rappresentata attraverso un numero naturale tra 1 e 18 in ordine decrescente, dove più è piccolo il numero, maggiore sarà la priorità; per esempio un operatore con priorità 1 avrà maggiore priorità su un operatore con priorità 13.
Nell'esempio che segue è possibile osservare una semplice applicazione pratica delle precedenze:
#include <iostream>
using namespace std;
int main () {
int a = 5;
int b = 6;
int c = 8;
int op = a+b*c;
cout << op << endl; // Stampa a schermo
return 0;
}
L'operatore moltiplicazione (*
) possiede priorità 5, mentre l'operatore addizione (+
) possiede priorità 6; pertanto il risultato a schermo sarà 53 e non 19.
Associatività degli operatori
L'associatività indica l'ordine in cui vengono eseguiti gli operatori aventi la stessa priorità tra loro[12]. Viene riportato un semplice esempio con gli operatori di divisione (priorità 5).
#include <iostream>
using namespace std;
int main (){
float a = 5.0;
float b = 6.0;
float c = 8.0;
float div = a/b/c;
cout << div << endl; // Stampa a schermo
return 0;
}
Nell'esempio precedente la stampa a schermo sarà 0.104167 perché l'operatore di divisione segue associatività a sinistra e, quindi, il calcolatore avrà eseguito la seguente equazione: .
Tabella delle precedenze e delle associatività
Nella tabella seguente sono elencati gli operatori di C e C++ in ordine di priorità con le loro rispettive associatività. Essi sono sempre implementabili su C++, mentre, quelli appositamente indicati nella penultima colonna da sinistra, non lo sono in C[12][17].
Priorità | Operatore | Nome | Implementabile in C | Associatività |
---|---|---|---|---|
1
(priorità massima) |
class-name :: member
|
Risolutore di visibilità | No | sinistra |
:: member
|
Risolutore globale di visibilità | No | destra | |
2 | object . member
|
Selezione | Sì | sinistra |
pointer -> member
|
Deferenziazione e selezione | Sì | sinistra | |
array[ rvalue ]
|
Indicizzazione | Sì | sinistra | |
function ( actual-argument-list )
|
Chiamata di funzione | Sì | sinistra | |
lvalue ++
|
Postincremento | Sì | destra | |
lvalue --
|
Postdecremento | Sì | destra | |
static_cast type<rvalue>
|
Conversione di tipo | No | sinistra | |
const_cast type<rvalue>
|
Conversione const | No | sinistra | |
3 | sizeof object
|
Dimensione
di oggetto di tipo |
Sì | destra |
++ lvalue
|
Preincremento | Sì | destra | |
-- lvalue
|
Predecremento | Sì | destra | |
~ rvalue
|
Complemento bit a bit | Sì | destra | |
! rvalue
|
Negazione | Sì | destra | |
- rvalue
|
Meno unitario | Sì | destra | |
+ rvalue
|
Più unitario | Sì | destra | |
& rvalue
|
Indirizzo | Sì | destra | |
* rvalue
|
Deferenziazione | Sì | destra | |
new type
|
Allocazione | No | destra | |
new type[]
|
Allocazione di array | No | destra | |
delete pointer
|
Deallocazione | No | destra | |
delete[] pointer
|
Deallocazione di array | No | destra | |
4 | object .* pointer-to-member
|
Selezione con puntatore a membro | No | sinistra |
pointer ->* pointer-to-member
|
Deferenziazione e selezione con puntatore a membro | No | sinistra | |
5 | rvalue * rvalue
|
Moltiplicazione | Sì | sinistra |
rvalue / rvalue
|
Divisione | Sì | sinistra | |
rvalue % rvalue
|
Modulo | Sì | sinistra | |
6 | rvalue + rvalue
|
Addizione | Sì | sinistra |
rvalue - rvalue
|
Sottrazione | Sì | sinistra | |
7 | rvalue << rvalue
|
Traslazione sinistra | Sì | sinistra |
rvalue >> rvalue
|
Traslazione destra | Sì | sinistra | |
8 | rvalue < rvalue
|
Minore | Sì | sinistra |
rvalue <= rvalue
|
Minore o uguale | Sì | sinistra | |
rvalue > rvalue
|
Maggiore | Sì | sinistra | |
rvalue >= rvalue
|
Maggiore o uguale | Sì | sinistra | |
9 | rvalue == rvalue
|
Uguale | Sì | sinistra |
rvalue != rvalue
|
Diverso | Sì | sinistra | |
10 | rvalue & rvalue
|
AND bit a bit | Sì | sinistra |
11 | rvalue ^ rvalue
|
OR esclusivo bit a bit | Sì | sinistra |
12 | rvalue | rvalue
|
OR bit a bit | Sì | sinistra |
13 | rvalue && rvalue
|
AND logico | Sì | sinistra |
14 | rvalue || rvalue
|
OR logico | Sì | sinistra |
15 | co_await
|
Coroutine | No | destra |
16 | rvalue ? rvalue : rvalue
|
Espressione condizionale | Sì | sinistra |
17 | lvalue = rvalue
|
Assegnamento | Sì | destra |
lvalue += rvalue
|
Addizione e assegnamento | Sì | destra | |
lvalue -= rvalue
|
Sottrazione e assegnamento | Sì | destra | |
lvalue *= rvalue
|
Moltiplicazione e assegnamento | Sì | destra | |
lvalue /= rvalue
|
Divisione e assegnamento | Sì | destra | |
lvalue %= rvalue
|
Modulo e assegnamento | Sì | destra | |
lvalue &= rvalue
|
AND bit a bit e assegnamento | Sì | destra | |
lvalue |= rvalue
|
OR bit a bit e assegnamento | Sì | destra | |
lvalue ^= rvalue
|
OR esclusivo bit a bit e assegnamento | Sì | destra | |
lvalue <<= rvalue
|
Traslazione a sinistra e assegnamento | Sì | destra | |
lvalue =>> rvalue
|
Traslazione a destra e assegnamento | No | destra | |
18
(priorità minima) |
expression , expression
|
Virgola | Sì | sinistra |
Critiche sulle precedenze degli operatori
La precedenza degli operatori logici bitwise è stata al centro di numerose critiche[18][19], poiché concettualmente, &
e |
sono operatori aritmetici come *
e +
.
Per esempio, l'espressione a & b == 7
viene sintatticamente analizzata come a & (b == 7)
, mentre l'espressione a + b == 7
viene analizzata come (a + b) == 7
. Ciò richiede l'uso delle parentesi più spesso di quanto sarebbe altrimenti necessario.
Storicamente, non esisteva una distinzione sintattica tra gli operatori bit a bit e quelli logici. Nei linguaggi BCPL, B e nelle prime versioni di C, gli operatori && e || non esistevano proprio; invece, & |
aveva un significato diverso a seconda che venisse usato in un “contesto di valore di verità” (cioè quando ci si aspettava il ritorno di un valore booleano, come in if (a==b & c) {...}
e si comportava come un operatore logico, ma in c = a & b
si comportava invece come un operatore bit a bit).
La sintassi attuale è stata mantenuta soltanto per consentire la retrocompatibilità con le installazioni già esistenti.
Peraltro, in C++ (e nelle versioni successive di C) le operazioni di uguaglianza, con l'eccezione dell'operatore di confronto logico a tre, producono valori di tipo bool che sono concettualmente un singolo bit (1 o 0) e come tali non appartengono propriamente alle operazioni bit a bit.
Sinonimi in C++
C++ definisce una serie di keywords che possono essere implementate come sinonimi rispetto agli operatori[20]; tali definizioni non sono assolutamente implementabili nel linguaggio C.
Keyword | Operator |
---|---|
and
|
&&
|
and_eq
|
&=
|
bitand
|
&
|
bitor
|
|
|
compl
|
~
|
not
|
!
|
not_eq
|
!=
|
or
|
||
|
or_eq
|
|=
|
xor
|
^
|
xor_eq
|
^=
|
Questi possono essere utilizzati esattamente come sostituti dei rispettivi simboli di punteggiatura; ciò significa, per esempio, che le espressioni (a > 0 AND NOT flag)
e (a > 0 && flag)
avranno un significato assolutamente identico.
Note
- ^ a b Luis Joyanes Aguilar, Fondamenti di programmazione in C++. Algoritmi, strutture dati e oggetti, II edizione in lingua italiana, Madrid-Milano, McGraw-Hill Education, 2008, ISBN 978-88-386-6477-9.
- ^ Stanley Lippman, Josée Lajoie e Barbara Moo, C++ Primer, V edizione, Addison-Wesley Professional, 2013, ISBN 978-0321714114.
- ^ Conversione di tipo, su HTML.it. URL consultato il 14 settembre 2024.
- ^ Il linguaggio C#, su www.andreaminini.com. URL consultato il 30 agosto 2024.
- ^ Operator Overloading - D Programming Language, su dlang.org. URL consultato il 30 agosto 2024.
- ^ 1.3 Operatori., su www.math.unipd.it. URL consultato il 30 agosto 2024.
- ^ Operatori booleani, su HTML.it. URL consultato il 30 agosto 2024.
- ^ (EN) C++ | Definition, History, & Facts | Britannica, su www.britannica.com. URL consultato il 30 agosto 2024.
- ^ L'accezione booleana è stata coniata in onore del matematico George Boole.
- ^ isocpp.org, https://isocpp.org/wiki/faq/operator-overloading .
- ^ Linguaggio c++ - Enciclopedia, su Treccani. URL consultato il 30 agosto 2024.
- ^ a b c d Andrea Domenici e Graziano Frosini, Introduzione alla programmazione ed elementi di strutture dati con il linguaggio C++, collana "Informatica", diretta da A. L. Frisiani, VIII edizione, Milano/Pisa, ISBN 978-8846462022.
- ^ Per le conversioni definite dall'utente, il tipo di ritorno corrisponde implicitamente e necessariamente al nome dell'operatore, a meno che il tipo non sia dedotto. (es.
operator auto(), operator decltype(auto)()
etc.). - ^ Marco Cococcioni, Le classi nel linguaggio C++ (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
- ^ Marco Cococcioni, Programmazione in stile C, utilizzando il linguaggio C++ (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
- ^ The ISO C 1999 standard, section 6.5.6 note 71 (Technical report). ISO. 1999.
- ^ a b Marco Cococcioni, Priorità degli operatori (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
- ^ Chistory, su www.bell-labs.com. URL consultato il 30 agosto 2024.
- ^ Re^10: next unless condition, su www.perlmonks.org. URL consultato il 30 agosto 2024.
- ^ ISO/IEC 14882:1998(E) Programming Language C++. open-std.org – The C++ Standards Committee. 1 September 1998. pp. 40–41.
Bibliografia
- (EN) Bjarne Stroustrup, The C++ Programming Language, Addison-Wesley, ISBN 978-0-201-70073-2.
- Andrea Domenici, Graziano Frosini, Introduzione alla programmazione ed elementi di strutture dati con il linguaggio C++, Franco Angeli Editore, 2013, p. 480, ISBN 978-88-464-6202-2.
- Marco Cococcioni, Fondamenti di programmazione (PDF), Dipartimento di Ingegneria dell'Informazione dell'Università di Pisa.
- (ES) Luis Joyanes Aguilar, Fondamenti di programmazione in C++. Algoritmi, strutture dati e oggetti, Madrid-Milano, McGraw-Hill Education, ISBN 978-88-386-6477-9.
Voci correlate
Collegamenti esterni
- Volle, Adam. "C++". Encyclopedia Britannica, 19 Jun. 2024, https://www.britannica.com/technology/C-computer-language;
- Microsoft C++, C, and Assembler documentation.