Fondamenti di Informatica II Ingegneria Informatica / Automatica (A-I) Meccanica Prof. M.T. PAZIENZA a.a – 3° ciclo
Overloading Il meccanismo di overloading consente di avere funzioni (ed operatori) con lo stesso nome, ma con argomenti in numero o di tipo diverso. Ciò permette di utilizzare, su oggetti di nuove classi, operatori definiti per i tipi di dati predefiniti ma modificandoli per adattarli agli oggetti delle nuove classi in modo che si comportino nel modo più appropriato.
Overload degli operatori L'overload degli operatori serve ad applicare gli operatori anche ai tipi astratti. Per operatori e funzioni in overload, il C++ risolve l'ambiguità in base al contesto degli operandi, riconoscendone il tipo e decidendo di conseguenza quale operatore applicare. Bisogna creare una funzione con nome operator seguito dal simbolo dell'operatore (es.: operator +).
Overloading degli operatori Esempio: L’operatore di addizione (+) opera in modo diverso su valori int, float, double, perché questo operatore è definito in overlaoding (nello stesso linguaggio C++).
Overloading degli operatori Se l'operatore si applicherà a una classe, la funzione può essere definita: –come metodo (pubblico) della classe, –come esterna, nel qual caso va inserita come friend nella definizione della classe Nel primo caso, nella traduzione interna della chiamata, viene aggiunto il puntatore nascosto this all'oggetto in cui la funzione é incapsulata. la funzione può essere un metodo solo se il primo operando è l'oggetto stesso in caso contrario la funzione deve essere friend esterna.
Esempio Data la classe A, si vuole creare due overload dell'operatore "+" perché abbiano significato le operazioni 1) A + int 2) int + A (possibilmente con lo stesso risultato, per mantenere valida la proprietà commutativa dell'operazione). In entrambi i casi le funzioni devono restituire un oggetto della classe A, ma, mentre nel primo caso il primo operando è anch'esso oggetto della classe A e quindi la funzione può essere metodo della classe stessa, nel secondo caso no e pertanto la funzione deve essere esterna: 1) A A::operator+(int) 2) friend A operator+(int, A);
Overload degli operatori di I/O Un caso particolare rappresenta l'overload degli operatori di flusso " >" (estrazione). Un'operazione di output viene eseguita tramite l'operatore <<, che "inserisce" nell'oggetto cout (primo operando) il dato da scrivere (secondo operando), il quale può essere di qualunque tipo nativo (sono riconosciute anche le stringhe). Si vuole estendere l'operazione anche ai tipi astratti, per esempio per far sì che l'operazione: cout << a; (dove a è un'istanza di una classe A) generi su video una stampa dei valori assunti dai membri di a.
Overload di << cout, oggetto globale generato all'inizio dell'esecuzione del programma, é un'istanza della classe ostream, che viene detta "classe di flusso di output" (e dichiarata in ). Il primo argomento della funzione dovrà essere lo stesso oggetto cout, mentre il secondo argomento dovrà essere l'oggetto a da trasferire in output. La funzione dovrà restituire lo stesso cout, per permettere l'associazione di ulteriori operazioni nella stessa istruzione.
Overload di << La funzione per l'overload di << dovrà essere così definita: ostream& operator<<(ostream& out, const A& a) nella funzione ci saranno istruzioni del tipo: out << a.ma; (dove ma è un membro di a) e l'istruzione di ritorno sarà: return out. Notare: –il primo argomento della funzione appartiene a ostream e non ad A e quindi deve essere definita come funzione esterna friend di A; –il C++ non ammette la creazione di copie dell'oggetto cout; –grazie all'associatività dell'operatore << (da sinistra a destra), si possono impilare più operazioni di output in una stessa istruzione (es. cout << a1 << a2 << a3; )
Overload di >> Analogamente, si può definire un overload dell'operatore di estrazione ">>" per le operazioni di input (del tipo, per esempio, cin >> a;), tramite la funzione: istream& operator>>( istream& inp, A& a) dove istream è la classe di flusso di input (anch'essa dichiarata in ), a cui appartiene l'oggetto globale cin.
Overload dell’operatore di assegnazione L’operatore di assegnazione è un operatore binario, che, nel suo significato naturale, copia il secondo operando nella locazione di memoria rappresentata dal primo. In assenza di overload, l'operatore di assegnazione funziona in C++ anche per le classi, nel senso che esegue una copia degli oggetti membro a membro. Se alcuni membri sono puntatori, la semplice copia genera due problemi: –dopo la copia, l'area precedentemente puntata dal primo operando resta ancora, cioè occupa spazio, ma non è più accessibile; –il fatto che due oggetti puntino alla stessa area è pericoloso, perché, se viene chiamato il distruttore per uno dei due, l'altro si ritrova a puntare a un'area dello heap che non è più disponibile.
Overload operatore di assegnazione E' necessario in questi casi che l'operatore di assegnazione non esegua la copia del puntatore, ma dell'area puntata, che deve essere allocata separatamente dalla prima con l'operatore new (e quindi il contenuto dei due puntatori risulterà diverso). L'area precedentemente puntata dal primo operando deve essere deallocata con l'operatore delete.
Overload operatore di assegnazione Per l'esecuzione di istruzioni del tipo: a2 = a1; (dove a1 e a2 sono istanze di una classe A costituita da un solo membro pa, puntatore a int), il corretto overload dell'operatore di assegnazione potrebbe essere: A& A::operator=(const A& a) { if ( this == &a ) return *this; delete [ ] pa; pa = new int; *pa = *a.pa; return *this; }
Esercizio class Complex { double re; double im; public: Complex(double, double); Complex operator+(const double&); Complex operator-(const double&); Complex operator+(const Complex&); Complex operator-(const Complex&); friend Complex operator+(const double&, const Complex&); int operator==(const Complex&); int operator!=(const Complex&); friend ostream& operator<<(ostream&, const Complex&); } ;
Esercizio Complex::Complex(double re0, double im0) { re = re0; im = im0; } Complex Complex::operator+(const double& k) { Complex temp; temp.re = re+k; temp.im = im; return temp; } Complex Complex::operator-(const double& k){ return *this + (-k); }
Esercizio Complex Complex::operator+(const Complex& p) { Complex temp; temp.re = re + p.re; temp.im = im + p.im; return temp; } Complex Complex::operator-(const Complex& p) { Complex temp; temp.re = re-p.re; temp.im = im-p.im; return temp; }
Esercizio int Complex::operator==(const Complex& p) { return (re==p.re && im==p.re) ? 1 : 0 ; } int Complex::operator!=(const Complex& p) { return !(*this==p); } Complex operator+(const double& k, const Complex& p) { return p+k; } ostream& operator<<(ostream& out, const Complex& c);() { out << "(" << c.re << "," << c.im << ")" << endl; return out; }
Esercizio void main() { double re0,im0; cin >> re0 >> im0 ; Complex p1(re0,im0); cin >> re0 >> im0 ; Complex p2(re0,im0); if ( p1==p2 ) cout << "UGUALI\n"; if ( p1!=p2 ) cout << "DIVERSI\n"; Complex temp = p1+p2; cout << "Somma:" ; temp.show() ; temp = p1-p2; cout << "Differenza:" ; temp.show() ; }