funzioni integrate e sovraccaricabili Da Wikipedia, l'enciclopedia libera
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].
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].
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. |
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); |
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); |
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); |
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); |
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. |
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); |
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].
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].
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.
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
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.
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: .
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 |
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.
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.
Seamless Wikipedia browsing. On steroids.