Elementi di
programmazione
ad oggetti
a. a. 2009/2010
Corso di Laurea Magistrale in Ingegneria Elettronica
Docente: Mauro Mazzieri, Dipartimento di Ingegneria Informatica,
Gestionale e dell’Automazione
Lezione 5
Sovraccarico degli operatori
Sovraccarico degli operatori
Il meccanismo del sovraccarico delle
funzioni vale anche per gli operatori
predefiniti
Gli operatori non sono altro che funzioni
in forma infissa
a
*= 5 equivale a a.operator*=(5)
Il sovraccarico consente di ridefinire il
comportamento degli operatori
predefiniti
Non
è possibile creare nuovi operatori
Operatori ridefinibili
È possibile ridefinire gli operatori
+ - * / % ^ & | ~ ! = < > +=
-= *= /= %= ^= &= |= << >>
>>= <<= == != <= >= && || ++
-- ->* , -> [] () new new[]
delete delete[]
Non è possibile ridefinire gli operatori
:: . .* ?: sizeof typeid
Definire un operatore
Affinché un operatore possa operare sui
membri di una classe deve essere ridefinito
Fanno eccezione
Operatore di indirizzo &
Operatore di assegnamento =
Restituisce l’indirizzo dell’oggetto
Esegue una copia membro a membro
Entrambi possono comunque essere ridefiniti
Non è possibile ridefinire operatori che
operano sui tipi predefiniti
Non è possibile cambiare l’arità o
l’associatività di un operatore
Associatività degli operatori
Per tutti gli operatori l’associatività è
da sinistra a destra
Tranne = per cui l’associatività è da
destra a sinistra
z+y+z viene valutata come (x+y)+z
x=y=z viene valutata come x=(y=z)
Assegnamento membro a
membro
L’assegnamento di un oggetto ad un altro
oggetto viene gestito di default come una
copia membro a membro
unOggetto = unAltroOggetto;
Non viene utilizzato il costruttore per copia ma
un operatore implicito di assegnamento
Ad ogni membro non statico di unOggetto viene
assegnato il corrispondente valore del membro di
unAltroOggetto
Dopo la copia, i due oggetti hanno gli stessi
dati, ma rimangono due oggetti distinti
Definizione di un operatore
Il sovraccarico di un operatore si esegue
definendo una funzione il cui nome è
operator<operatore>
class Complex
{
double re, im;
public:
Complex& operator+(Complex c) {
re += c.re;
im += c.im;
return *this;
}
};
Sovraccarico di un operatore
unario
Un operatore unario può essere definito
come funzione membro (senza argomenti)
o funzione non membro (con un
argomento)
L’argomento è un oggetto della classe o un suo
riferimento
È necessario che sia un riferimento quando si deve
modificare l’oggetto
Complex& Complex::operator-() {
re = -re;
im = -im;
return *this;
}
Sovraccarico di un operatore
binario
Un operatore binario può essere definito come
funzione membro (con un argomento) o funzione
non membro (con due argomenti)
Gli argomenti sono oggetti della classe o loro riferimenti
È necessario che sia un riferimento quando si deve
modificare l’oggetto
Quando l’operatore viene definito tramite una funzione
membro, l’oggetto alla sinistra dell’operatore invoca
l’operatore e quello sulla destra viene passato come
argomento
Complex& Complex::operator-(Complex& c) {
re -= c.re;
im -= c.im;
return *this;
}
Valore di ritorno di un operatore
Un operatore può restituire una copia dell’oggetto
risultato o un riferimento
Complex& Complex::operator-(Complex& c) {
re -= c.re;
im -= c.im;
return *this;
}
Complex Complex::operator-(Complex& c) {
re -= c.re;
im -= c.im;
return *this;
}
Operatori friend
Un operatore che ha bisogno di accedere ai
dati privati di un oggetto può essere
definito come friend invece che come
membro
Le funzioni friend non hanno un puntatore
this, per cui bisogna passare esplicitamente
entrambi i parametri
Gli operatori friend sono utili in quei casi in
cui l’operazione riguarda oggetti di tipi
diversi
Es. * vettore-matrice, - magazzino-ordine
Sovraccarico di << e >>
Gli operatori << e >> (inserimento ed estrazione da uno stream)
vanno ridefiniti tramite operatori friend per poterli usare su oggetti
definiti dall’utente
class Complex
{
// …
friend std::ostream& operator<<(std::ostream&, const
Complex);
friend std::istream& operator>>(std::istream&,
Complex&);
};
std::ostream& operator<<(std::ostream& o, const Complex
c) {
return o << "(" << c.re << ", " << c.im << ")";
}
std::istream& operator>>(std::istream& i, Complex& c) {
i >> c.re >> c.im;
return i;
}
Operatori new e delete
new e delete sono degli operatori, ed in quanto tali
possono essere ridefiniti
void* operator new(size_t size)
{
// Esegue l'allocazione.
// Restituisce un puntatore alla memoria
allocata, oppure 0
}
void operator delete(void* p)
{
// rilascia la memoria
}
Quando viene chiamato new, viene automaticamente
chiamato il costruttore appropriato
Quando viene chiamato delete, viene automaticamente
chiamato il distruttore
new e delete su array
void* operator new[](size_t size)
{
// Esegue l'allocazione.
// Restituisce un puntatore alla
memoria allocata, oppure 0
}
void operator delete[](void* p)
{
// Rilascia la memoria.
// Il distruttore di ogni
elemento viene chiamato
automaticamente
}
Operatore []
Esistono due versioni dell’operatore []
const e non const
Passare
ad una funzione un array const e
poi utilizzare l’operatore per accedere al
contenuto modificandolo sarebbe un
problema
Il compilatore non consente l’uso di funzioni
membro non const con oggetti const