Uno dei costrutti del C++ più potenti è la possibilità di ridefinire il significato di un operatore che interviene fra due classi, in questo articolo tratteremo dell’overloading degli operatori.
Gli operatori che è più utile ridefinire sono:
Per cominciare prenderemo in esame gli operatori più semplici da trattare, quelli aritmetici, rinviando ad un prossimo articolo quelli più complicati come gli smart pointer.
Ci sono due modi per definire un operatore: scrivere una funzione che ha come nome la keyword operator seguita dall’operatore stesso
class operator+(const class& c1, const class& c2)
oppure, ma solo quando il primo operando è un oggetto, tramite una funzione membro della classe di quell’oggetto
public:
class& operator+(const class& c)
per gli operatori unari è possibile utilizzare solo la seconda soluzione. Quando utilizzare la funzione globale o quella membro? Generalmente è consigliabile quando possibile quella globale perché si applicano ad entrambi tutte le conversioni di tipo definite dall’utente. Vediamo allora un semplice utilizzo dell’overloading degli operatori con una nostra classe per definire un punto su un asse cartesiano, chiamiamola “CMyPunto”.
// File Corso.h class CMyPunto { private: public: int x, y; CMyPunto(int x0, int y0) { x = x0; y = y0; }; void operator+=(const CMyPunto& pt); void Stampa(); }; CMyPunto operator+(const CMyPunto& pt1, const CMyPunto& pt2); bool operator==(const CMyPunto& pt1, const CMyPunto& pt2); // File Corso.cpp #include <iostream> #include "Corso.h" using namespace std; void CMyPunto::Stampa() { cout << "Il valore del punto" << endl; cout << "x: " << x << endl; cout << "y: " << y << endl << endl; } CMyPunto operator+(const CMyPunto& pt1, const CMyPunto& pt2) { CMyPunto ptTemp(0, 0); ptTemp.x = pt1.x + pt2.x; ptTemp.y = pt1.y + pt2.y; return ptTemp; } void CMyPunto::operator+=(const CMyPunto& pt) { x += pt.x; y += pt.y; } bool operator==(const CMyPunto& pt1, const CMyPunto& pt2) { if (pt1.x == pt2.x && pt1.y == pt2.y) return true; return false; } int main(int argc, char** args) { CMyPunto pt1(12, 7); CMyPunto pt2(24, 18); (pt1 + pt2).Stampa(); pt1 += pt2; pt1.Stampa(); if (pt1 == pt2) cout << "Sono uguali" << endl << endl; else cout << "Sono diversi" << endl << endl; int k; cin >> k; return 0; }
In C++ c’è uno strano caso sul quale mi voglio soffermare prima di terminare questo articolo. Avete mai pensato alla diferrenza degli operatori unari di incremento ( ++ ) o di decremento ( — ) postfisso o prefisso? Ad esempio queste due righe sono uguali?
i++; // postfisso
++i; // prefisso
Vediamo con l’uso dell’overloading degli operatori le differenze di implementazione:
// File Corso.h class CMyPunto { private: public: int x, y; CMyPunto(int x0, int y0) { x = x0; y = y0; }; CMyPunto& operator++(); // prefisso CMyPunto operator++(int); // postfisso void Stampa(); }; // File Corso.cpp #include <iostream> #include "Corso.h" using namespace std; void CMyPunto::Stampa() { cout << "Il valore del punto" << endl; cout << "x: " << x << endl; cout << "y: " << y << endl << endl; } CMyPunto& CMyPunto::operator++() { x++; y++; return *this; } CMyPunto CMyPunto::operator++(int) { x++; y++; return *this; } int main(int argc, char** args) { CMyPunto pt(12, 7); pt++; pt.Stampa(); ++pt; pt.Stampa(); int k; cin >> k; return 0; }
Per distinguere l’operatore prefisso da quello postfisso è necessario utilizzare una sintassi differente, infatti la versione normale degli operatori è quella prefissa che restituiscono un reference, la versione postfissa si differenzia per un parametro dummy di tipo int e restituisce una copia dell’oggetto. La strana sintassi è stata giustificata affermando che l’operatore unario postfisso può essere visto come un operatore binario il cui primo operando è di tipo Iterator ed il secondo, invisibile, è di tipo int. Da notare che la versione postfissa restituisce il risultato per valore, mentre la forma prefissa lo restituisce per reference, quindi questa seconda forma è più efficiente della prima. Da questo ragionamento conviene scrivere
for (int i = 0; i < 100; ++i)
piuttosto che
for (int i = 0; i < 100; i++)
In un prossimo articolo continueremo la trattazione dell’overloading degli operatori parlando di double dispatching ed allocazione di memoria.
Sono arrivato alla convinzione che un abbonamento per tutti i miei software gestionali sia il…
MerciGest è un software per la gestione del magazzino completamente gratuito. Continua a leggere→
In ufficio può capitare di doversi allontanare dal proprio posto di lavoro, ecco che allora…
In questo articolo vedremo quando è più o meno utile togliere la corrente ad un…
Dopo la pausa invernale dovuta al lavoro che devo fare per sostentarmi, eccomi di nuovo…
Vediamo come eliminare i files direttamente da Windows senza utilizzare il cestino. Continua a leggere→