Overloading: differenze tra le versioni
Riga 44: | Riga 44: | ||
==Overloading degli operatori== |
==Overloading degli operatori== |
||
Prendiamo in considerazione il seguente esempio [[C++]]: una classe che rappresenta i [[numeri complessi]] (sono omesse le implementazioni del file .cpp) |
Prendiamo in considerazione il seguente esempio [[C++]]: una classe che rappresenta i [[numeri complessi]] (sono omesse le implementazioni del file .cpp) |
||
<source lang="Cpp"> |
|||
<nowiki> |
|||
class Complex |
|||
{ |
|||
private: |
|||
double _r; |
|||
double _i; |
|||
public: |
|||
Complex(int r, int i); |
|||
Complex(float r, float i); |
|||
Complex(double r, double i); |
|||
Complex(Complex c); |
|||
} |
|||
</source> |
|||
Vorremmo essere in grado di eseguire sui numeri complessi le stesse operazioni che eseguiamo normalmente sui [[numeri reali]]. Ad esempio, vorremmo poter eseguire il seguente codice: |
Vorremmo essere in grado di eseguire sui numeri complessi le stesse operazioni che eseguiamo normalmente sui [[numeri reali]]. Ad esempio, vorremmo poter eseguire il seguente codice: |
||
<source lang="Cpp"> |
|||
<nowiki> |
|||
Complex c,d; |
|||
... |
|||
if (c < d) |
|||
.... |
|||
... |
|||
cout << c; |
|||
</source> |
|||
Tale codice solleverebbe un errore di [[compilazione]], in quanto il [[compilatore]] non è in grado di valutare da solo se il complesso c è minore di d, né tantomeno di scriverlo a video. La soluzione, adottabile '''solo''' in [[C++]] e [[C sharp|C#]], è quella di definire un opportuno ''sovraccarico'' per gli operatori ''<'' e ''<<''. Per fare ciò è necessario conoscere come il compilatore dei linguaggi C tratta le espressioni con operatore. Esso le traduce con una chiamata a subroutine ''operator?()'', dove al posto di ''?'' va il simbolo dell'operatore. Ecco quindi i prototipi (e un paio di realizzazioni) di una completa famiglia di overloading per gli operatori principali di confronto e output. |
Tale codice solleverebbe un errore di [[compilazione]], in quanto il [[compilatore]] non è in grado di valutare da solo se il complesso c è minore di d, né tantomeno di scriverlo a video. La soluzione, adottabile '''solo''' in [[C++]] e [[C sharp|C#]], è quella di definire un opportuno ''sovraccarico'' per gli operatori ''<'' e ''<<''. Per fare ciò è necessario conoscere come il compilatore dei linguaggi C tratta le espressioni con operatore. Esso le traduce con una chiamata a subroutine ''operator?()'', dove al posto di ''?'' va il simbolo dell'operatore. Ecco quindi i prototipi (e un paio di realizzazioni) di una completa famiglia di overloading per gli operatori principali di confronto e output. |
||
NOTA: in una applicazione reale è richiesto l'utilizzo della [[libreria]] Math |
NOTA: in una applicazione reale è richiesto l'utilizzo della [[libreria]] Math |
||
<source lang="Cpp"> |
|||
<nowiki> |
|||
//operator< |
|||
int operator<(Complex a, Complex b) |
|||
{ |
|||
return (sqrt(a.r*a.r+a.i*a.i)<sqrt(b.r*b.r+b.i*b.i)); |
|||
} |
|||
int operator<(Complex a, double b) |
|||
⚫ | |||
{ |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
int operator<(Complex a, float b); |
|||
int operator<(Complex a, int b); |
|||
⚫ | |||
int operator<(float a, Complex b); |
|||
int operator<(int a, Complex b); |
|||
//operator> |
|||
int operator>(Complex a, Complex b) |
|||
{ |
|||
return (sqrt(a.r*a.r+a.i*a.i)>sqrt(b.r*b.r+b.i*b.i)); |
|||
} |
|||
⚫ | |||
⚫ | |||
⚫ | |||
{ |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
int operator>(Complex a, float b); |
|||
int operator>(Complex a, int b); |
|||
⚫ | |||
⚫ | |||
⚫ | |||
//operator<< |
|||
ostream& operator<<(ostream& out, Complex c) |
|||
{ |
|||
cout << c.r; |
|||
if (c.i > 0) |
|||
cout << "+"; |
|||
cout << c.i << "i"; |
|||
} |
|||
</source> |
|||
In linguaggio [[Visual Basic]] è possibile simulare il sovraccarico degli operatori implementando l'[[interfaccia]] IComparable. |
In linguaggio [[Visual Basic]] è possibile simulare il sovraccarico degli operatori implementando l'[[interfaccia]] IComparable. |
||
Versione delle 16:20, 24 mar 2008
In programmazione, si dice overloading una famiglia di funzioni/subroutine aventi lo stesso nome, ma con la possibilità di accettare un diverso set di argomenti (signature), ed eventualmente restituire un diverso valore di ritorno. Tale famiglia di funzioni è detta in rapporto di Overloading, o sovraccaricata.
A seconda dei casi, si può parlare di overloading di funzioni, di costruttori e di operatori. Sovraccaricare il costruttore di una classe è una pratica comune per gli sviluppatori di librerie, in quanto permette loro di fornire allo sviluppatore finale diverse modalità per inizializzare la classe con determinati valori iniziali.
Overloading del costruttore e dei metodi delle classi
Ecco alcuni esempi di sovraccarico di un costruttore Persona e un metodo "Rubrica.Inserisci", in linguaggio Visual Basic (è omessa la dichiarazione di un tipo enumerativo "Sesso" di valore "Maschio/Femmina" e di alcune implementazioni)
Class Persona Private _nome As String Private _cognome As String Private _nascita As Date Private _sex As Sesso Public ReadOnly Property NomeCompleto() As String Public Overloads Sub New(ByVal nome As String, ByVal cognome As String, ByVal nascita As Date, ByVal sex As Sesso) _nome = nome _cognome = cognome _nascita = nascita _sex = sex End Sub Public Overloads Sub New(ByVal AltraPersona As Persona) _nome = altrapersona._nome _cognome = altrapersona._cognome _nascita = altrapersona._nascita _sex = altrapersona._sex End Sub End Class Class Rubrica [...] Public Overloads Sub Inserisci(ByVal Nome As Stirng, ByVal Cognome As String) Public Overloads Sub Inserisci(ByVal Item As Persona) End Class
In questo modo è possibile inizializzare un'istanza della classe Persona sia fornendo nome e cognome come stringa che copiandoli da un'altra istanza di Persona. Il codice di cui sopra è facilmente riutilizzabile in linguaggi come C, C++, C# e Java, riscrivendo il tutto secondo la sintassi del linguaggio scelto.
Overloading degli operatori
Prendiamo in considerazione il seguente esempio C++: una classe che rappresenta i numeri complessi (sono omesse le implementazioni del file .cpp)
class Complex
{
private:
double _r;
double _i;
public:
Complex(int r, int i);
Complex(float r, float i);
Complex(double r, double i);
Complex(Complex c);
}
Vorremmo essere in grado di eseguire sui numeri complessi le stesse operazioni che eseguiamo normalmente sui numeri reali. Ad esempio, vorremmo poter eseguire il seguente codice:
Complex c,d;
...
if (c < d)
....
...
cout << c;
Tale codice solleverebbe un errore di compilazione, in quanto il compilatore non è in grado di valutare da solo se il complesso c è minore di d, né tantomeno di scriverlo a video. La soluzione, adottabile solo in C++ e C#, è quella di definire un opportuno sovraccarico per gli operatori < e <<. Per fare ciò è necessario conoscere come il compilatore dei linguaggi C tratta le espressioni con operatore. Esso le traduce con una chiamata a subroutine operator?(), dove al posto di ? va il simbolo dell'operatore. Ecco quindi i prototipi (e un paio di realizzazioni) di una completa famiglia di overloading per gli operatori principali di confronto e output. NOTA: in una applicazione reale è richiesto l'utilizzo della libreria Math
//operator<
int operator<(Complex a, Complex b)
{
return (sqrt(a.r*a.r+a.i*a.i)<sqrt(b.r*b.r+b.i*b.i));
}
int operator<(Complex a, double b)
{
return (sqrt(a.r*a.r+a.i*a.i)<b);
}
int operator<(Complex a, float b);
int operator<(Complex a, int b);
int operator<(double a, Complex b);
int operator<(float a, Complex b);
int operator<(int a, Complex b);
//operator>
int operator>(Complex a, Complex b)
{
return (sqrt(a.r*a.r+a.i*a.i)>sqrt(b.r*b.r+b.i*b.i));
}
int operator>(Complex a, double b)
{
return (sqrt(a.r*a.r+a.i*a.i)>b);
}
int operator>(Complex a, float b);
int operator>(Complex a, int b);
int operator>(double a, Complex b);
int operator>(float a, Complex b);
int operator>(int a, Complex b);
//operator<<
ostream& operator<<(ostream& out, Complex c)
{
cout << c.r;
if (c.i > 0)
cout << "+";
cout << c.i << "i";
}
In linguaggio Visual Basic è possibile simulare il sovraccarico degli operatori implementando l'interfaccia IComparable.
Note sull'overloading
Un errore comune di molti programmatori è quello di voler creare due funzioni che accettano gli stessi tipi di parametri in ingresso e restituiscono un tipo differente. Ciò non è possibile perché l'esecutore identifica le funzioni e le subroutine, a livello assembly, mediante delle etichette (label). Tali label rispecchiano la signature della funzione stessa, e pertanto due label uguali non possono coesistere all'interno di uno stesso spazio di visibilità. È comunque possibile definire due o più funzioni dalla stessa signature all'interno di spazi di nomi diversi senza ottenere errori di compilazione.
L'overloading, infine, non influisce sulla corretta esecuzione delle procedure ricorsive.