Operazione Overloading
Per facilità di lettura e scrittura di codice, l'overloading di alcune operazioni è consentito. L' operatore overloading è scritto usando la parola chiave operator. I seguenti operatori possono essere sottoposti ad overload:
- binari +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^
- unari +,-,++,--,!,~
- operatore di assegnazione =
- operatore di indicizzazione []
L'operazione di overloading consente l'utilizzo della notazione operativa (scritta in forma di espressioni semplici) per gli oggetti complessi - strutture e classi. La scrittura di espressioni utilizzando le operazioni di overload semplifica la visualizzazione del codice sorgente, perché una implementazione più complessa è nascosta.
Per esempio, consideriamo numeri complessi, che sono composti dalla parte reale e quella immaginaria. Essi sono ampiamente utilizzati in matematica. Il linguaggio MQL5 non ha un tipo di dati per rappresentare numeri complessi, ma è possibile creare un nuovo tipo di dati nella forma di una struttura o classe. Dichiarare la struttura complessa e definire i quattro metodi che implementano quattro operazioni aritmetiche:
//+--------------------------------------------------------------------------------+
//| Una struttura per le operazioni con i numeri complessi |
//+--------------------------------------------------------------------------------+
struct complex
{
double re; // Parte reale
double im; // Parte immaginaria
//--- Costruttori
complex():re(0.0),im(0.0) { }
complex(const double r):re(r),im(0.0) { }
complex(const double r,const double i):re(r),im(i) { }
complex(const complex &o):re(o.re),im(o.im) { }
//--- Operazioni aritmetiche
complex Add(const complex &l,const complex &r) const; // Adizione
complex Sub(const complex &l,const complex &r) const; // Sottrazione
complex Mul(const complex &l,const complex &r) const; // Moltiplicazione
complex Div(const complex &l,const complex &r) const; // Divisione
};
|
Ora, nel nostro codice possiamo dichiarare variabili che rappresentano i numeri complessi, e lavorare con esse.
Ad esempio:
voidOnStart()
{
// --- Dichiara e inizializza le variabili del tipo complesso
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Sommare due numeri
complex z;
z=a.Add(a,b);
PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Moltiplicare due numeri
z=a.Mul(a,b);
PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Dividere due numeri
z=a.Div(a,b);
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
}
|
Ma sarebbe più conveniente utilizzare operatori consueti "+", "-", "*" e "/" per le operazioni aritmetiche ordinarie con numeri complessi.
L' operatore parola chiave viene utilizzato per la definizione di una funzione membro che esegue la conversione del tipo. Operazioni unarie e binarie per le variabili oggetto della classe possono essere sovraccaricate come funzioni membro non-statiche. Esse implicitamente agiscono sull' oggetto della classe.
Gran parte delle operazioni binarie possono essere sovraccaricate come funzioni regolari che accettano uno o entrambi gli argomenti come variabile della classe o un puntatore ad un oggetto di questa classe. Per il nostro tipo complesso, l'overloading nella dichiarazione sarà simile a questa:
//--- Operatori
complex operator+(const complex &r) const { return(Add(this,r)); }
complex operator-(const complex &r) const { return(Sub(this,r)); }
complex operator*(const complex &r) const { return(Mul(this,r)); }
complex operator/(const complex &r) const { return(Div(this,r)); }
|
L'esempio completo dello script:
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
//--- Dichiara ed inizializza le variabili di tipo complex
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//a.re=5;
//a.im=1;
//b.re=-1;
//b.im=-5;
//--- Sommare due numeri
complex z=a+b;
PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Moltiplica i due numeri
z=a*b;
PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Dividere due numeri
z=a/b;
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
}
//+--------------------------------------------------------------------------------+
//| Una struttura per le operazioni con i numeri complessi |
//+--------------------------------------------------------------------------------+
struct complex
{
double re; // Parte reale
double im; // Parte immaginaria
//--- Costruttori
complex():re(0.0),im(0.0) { }
complex(const double r):re(r),im(0.0) { }
complex(const double r,const double i):re(r),im(i) { }
complex(const complex &o):re(o.re),im(o.im) { }
//--- Operazioni aritmetiche
complex Add(const complex &l,const complex &r) const; // Adizione
complex Sub(const complex &l,const complex &r) const; // Sottrazione
complex Mul(const complex &l,const complex &r) const; // Moltiplicazione
complex Div(const complex &l,const complex &r) const; // Divisione
//--- Operatori Binari
complex operator+(const complex &r) const { return(Add(this,r)); }
complex operator-(const complex &r) const { return(Sub(this,r)); }
complex operator*(const complex &r) const { return(Mul(this,r)); }
complex operator/(const complex &r) const { return(Div(this,r)); }
};
//+--------------------------------------------------------------------------------+
//| Addizione |
//+--------------------------------------------------------------------------------+
complex complex::Add(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re+r.re;
res.im=l.im+r.im;
//--- Risultato
return res;
}
//+--------------------------------------------------------------------------------+
//| Sottrazione |
//+--------------------------------------------------------------------------------+
complex complex::Sub(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re-r.re;
res.im=l.im-r.im;
//--- Risultato
return res;
}
//+--------------------------------------------------------------------------------+
//| Moltiplicazione |
//+--------------------------------------------------------------------------------+
complex complex::Mul(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re*r.re-l.im*r.im;
res.im=l.re*r.im+l.im*r.re;
//--- Risultato
return res;
}
//+--------------------------------------------------------------------------------+
//| Divisione |
//+--------------------------------------------------------------------------------+
complex complex::Div(const complex &l,const complex &r) const
{
//--- Numeri complessi vuoti
complex res(EMPTY_VALUE,EMPTY_VALUE);
//--- Controlla per lo zero
if(r.re==0 && r.im==0)
{
Print(__FUNCTION__+": il numero è zero");
return(res);
}
//--- Variabili ausiliarie
double e;
double f;
//--- Seleziona variante di calcolo
if(MathAbs(r.im)<MathAbs(r.re))
{
e = r.im/r.re;
f = r.re+r.im*e;
res.re=(l.re+l.im*e)/f;
res.im=(l.im-l.re*e)/f;
}
else
{
e = r.re/r.im;
f = r.im+r.re*e;
res.re=(l.im+l.re*e)/f;
res.im=(-l.re+l.im*e)/f;
}
//--- Risultato
return res;
}
|
Gran parte delle operazioni unarie per le classi possono essere sovraccaricate come funzioni ordinarie che accettano un solo argomento di oggetto della classe o un puntatore ad essa. Aggiungere overloading di operazioni unarie "-" e "!".
//+--------------------------------------------------------------------------------+
//| Una struttura per le operazioni con i numeri complessi |
//+--------------------------------------------------------------------------------+
struct complex
{
double re; // Parte Reale
double im; // Parte Immaginaria
...
//--- Operatori Unari
complex operator-() const; // Meno unario
bool operator!() const; // Negazione
};
...
//+--------------------------------------------------------------------------------+
//| Fare l'Overloading dell'operatore "meno unario" |
//+--------------------------------------------------------------------------------+
complex complex::operator-() const
{
complex res;
//---
res.re=-re;
res.im=-im;
//--- Risultato
return res;
}
//+--------------------------------------------------------------------------------+
//| Fare l'Overloading dell'operatore "negazione logica" |
//+--------------------------------------------------------------------------------+
bool complex::operator!() const
{
//--- Le parti reale ed immaginaria del numero complesso sono pari a zero?
return (re!=0 && im!=0);
}
|
Ora siamo in grado di controllare il valore di un numero complesso per lo zero ed ottenere un valore negativo:
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
//--- Dichiara ed inizializza le variabili di tipo complex
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Divide i due numeri
complex z=a/b;
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//--- Un numero complesso è uguale a zero per default (nel costruttore di default re==0 ed im==0)
zero complesso;
Print("!zero=",!zero);
//--- Assegna un valore negativo
zero=-z;
PrintFormat("z=%.2f+i*%.2f, zero=%.2f+i*%.2f",z.re,z.im, zero.re,zero.im);
PrintFormat("-zero=%.2f+i*%.2f",-zero.re,-zero.im);
//--- Controlla per lo zero ancora una volta
Print("!zero=",!zero);
//---
}
|
Si noti che non abbiamo dovuto overloadare l'operatore di assegnazione "=", come nelle strutture di tipi semplici che possono essere copiate direttamente una dentro l'altra. Quindi, possiamo ora scrivere un codice per i calcoli che coinvolgono numeri complessi nel modo consueto.
L'overloading dell'operatore indicizzazione permette di ottenere i valori degli array racchiusi in un oggetto, in modo semplice e familiare, e contribuisce anche ad una migliore leggibilità del codice sorgente. Per esempio, abbiamo bisogno di fornire l'accesso ad un simbolo nella stringa nella posizione specificata. Una stringa in MQL5 è un tipo separato stringa, che non è un array di simboli, ma con l'aiuto di un' operazione di indicizzazione sovraccarico si può fornire un lavoro semplice e trasparente nella classe CString generata:
//+--------------------------------------------------------------------------------+
//| Classe per accedere ai simboli nella stringa come array di simboli |
//+--------------------------------------------------------------------------------+
class CString
{
string m_string;
public:
CString(string str=NULL):m_string(str) { }
ushort operator[] (int x) { return(StringGetCharacter(m_string,x)); }
};
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
void OnStart()
{
//--- Un array per ricevere i simboli da una stringa
int x[]={ 19,4,18,19,27,14,15,4,17,0,19,14,17,27,26,28,27,5,14,
17,27,2,11,0,18,18,27,29,30,19,17,8,13,6 };
CString str("abcdefghijklmnopqrstuvwxyz[ ]CS");
string res;
//--- Fa una frase utilizzando i simboli dalla variabile str
for(int i=0,n=ArraySize(x);i<n;i++)
{
res+=ShortToString(str[x[i]]);
}
//--- Mostra i risultati
Print(res);
}
|
Un altro esempio di sovraccarico della operazione di indicizzazione sono le operazioni con gli array. La matrice rappresenta un array bi-dimensionale dinamico, la dimensione della matrice non è definita in anticipo. Di conseguenza, non è possibile dichiarare un array di forma array [ ] [ ] senza specificare la dimensione della seconda dimensione, e quindi passare questo array come parametro. A possible solution is a special class CMatrix, which contains an array of CRow class objects.
//+--------------------------------------------------------------------------------+
//| Funzione di avvio del programma Script |
//+--------------------------------------------------------------------------------+
voidOnStart()
{
//--- Operazioni di addizzione e moltiplicazione di matrici
CMatrix A(3),B(3),C();
//--- Prepara un array per riga
double a1[3]={1,2,3}, a2[3]={2,3,1}, a3[3]={3,1,2};
double b1[3]={3,2,1}, b2[3]={1,3,2}, b3[3]={2,1,3};
//--- Riempie la matrice
A[0]=a1; A[1]=a2; A[2]=a3;
B[0]=b1; B[1]=b2; B[2]=b3;
//--- Da in output le matrici nel log degli Experts
Print("---- Elementi della matrice A");
Print(A.String());
Print("---- Elementi della matrice B");
Print(B.String());
//--- Addizione di matrici
Print("---- Addizione di matrici A e B");
C=A+B;
//--- Da in output la rappresentazione della stringa formattata
Print(C.String());
//--- Moltiplicazione di matrici
Print("---- Moltiplicazione delle matrici A e B");
C=A*B;
Print(C.String());
//--- Ora vi mostriamo come ottenere i valori nello stile di matrice array dinamica [i][j]
Print("Da in output il valore della matrice C secondo gli elementi");
//--- Va attraverso le righe della matrice - oggetti CRow - in loop
for(int i=0;i<3;i++)
{
string com="| ";
//--- Forma righe dalla matrice per i valori
for(int j=0;j<3;j++)
{
//--- Ottiene gli elementi della matrice dai numeri di righe e colonne
double element=C[i][j];// [i] - Accesso a CRow nell'array m_rows[] ,
// [j] - Operatore overloaded, di indicizazione in CRow
com=com+StringFormat("a(%d,%d)=%G ; ",i,j,element);
}
com+="|";
//--- Da in output i valori di row
Print(com);
}
}
//+--------------------------------------------------------------------------------+
//| Classe "Row" |
//+--------------------------------------------------------------------------------+
class CRow
{
private:
double m_array[];
public:
//--- Costruttori ed un distruttore
CRow(void) { ArrayResize(m_array,0); }
CRow(const CRow &r) { this=r; }
CRow(const double &array[]);
~CRow(void){};
//--- Numero di elementi nella riga
int Size(void) const { return(ArraySize(m_array));}
//--- Restituisce la stringa con valori
string String(void) const;
//--- Indicizzazione operatore
double operator[](int i) const { return(m_array[i]); }
//--- Assegnazione operatori
void operator=(const double &array[]); // Un array
void operator=(const CRow & r); // Un altro oggetto CRow
double operator*(const CRow &o); // Oggetto CRow per la moltiplicazione
};
//+--------------------------------------------------------------------------------+
//| Costruttore per inizializzare la riga con un array |
//+--------------------------------------------------------------------------------+
void CRow::CRow(const double &array[])
{
int size=ArraySize(array);
//--- Se l' array non è vuoto
if(size>0)
{
ArrayResize(m_array,size);
//--- Riempie con i valori
for(int i=0;i<size;i++)
m_array[i]=array[i];
}
//---
}
//+--------------------------------------------------------------------------------+
//| Operatore di assegnazione per l'array |
//+--------------------------------------------------------------------------------+
void CRow::operator=(const double &array[])
{
int size=ArraySize(array);
if(size==0) return;
//--- Riempie l'array con i valori
ArrayResize(m_array,size);
for(int i=0;i<size;i++) m_array[i]=array[i];
//---
}
//+--------------------------------------------------------------------------------+
//| Operatore di assegnazione per CRow |
//+--------------------------------------------------------------------------------+
void CRow::operator=(const CRow &r)
{
int size=r.Size();
if(size==0) return;
//--- Riempie l'array con i valori
ArrayResize(m_array,size);
for(int i=0;i<size;i++) m_array[i]=r[i];
//---
}
//+--------------------------------------------------------------------------------+
//| Operatore di moltiplicazione per un'altra riga |
//+--------------------------------------------------------------------------------+
double CRow::operator*(const CRow &o)
{
double res=0;
//--- Verifiche
int size=Size();
if(size!=o.Size() || size==0)
{
Print(__FUNCSIG__,": Fallimento nel moltiplicare le due matrici, le loro grandezze sono differenti");
return(res);
}
//--- Moltiplica l'array in base agli elementi ed aggiunge i prodotti
for(int i=0;i<size;i++)
res+=m_array[i]*o[i];
//--- Risultato
return(res);
}
//+--------------------------------------------------------------------------------+
//| Restituisce la rappresentazione in formato stringa |
//+--------------------------------------------------------------------------------+
string CRow::String(void) const
{
string out="";
//--- Se la grandezza dell'array è maggiore di zero
int size=ArraySize(m_array);
//--- Lavoriamo solo con un numero non-zero degli elementi dell'array
if(size>0)
{
out="{";
for(int i=0;i<size;i++)
{
//--- Raccoglie i valori in una stringa
out+=StringFormat(" %G;",m_array[i]);
}
out+=" }";
}
//--- Risultato
return(out);
}
//+--------------------------------------------------------------------------------+
//| Classe "Matrix" |
//+--------------------------------------------------------------------------------+
class CMatrix
{
private:
CRow m_rows[];
public:
//--- Costruttori ed un distruttore
CMatrix(void);
CMatrix(int rows) { ArrayResize(m_rows,rows); }
~CMatrix(void){};
//--- Ottiene le grandezze della matrice
int Rows() const { return(ArraySize(m_rows)); }
int Cols() const { return(Rows()>0? m_rows[0].Size():0); }
//--- Restituisce i valori della colonna sottoforma di una riga CRow
CRow GetColumnAsRow(const int col_index) const;
//--- Restituisce la stringa con i valori della matrice
string String(void) const;
//--- L'operatore di indicizzazione restituisce la stringa per il suo numero
CRow *operator[](int i) const { return(GetPointer(m_rows[i])); }
//--- Operatore addizone
CMatrix operator+(const CMatrix &m);
//--- Operatore Moltiplicazione
CMatrix operator*(const CMatrix &m);
//--- Operatore Assegnazione
CMatrix *operator=(const CMatrix &m);
};
//+--------------------------------------------------------------------------------+
//| Un costruttore di default, crea un array di righe di grandezza zero |
//+--------------------------------------------------------------------------------+
CMatrix::CMatrix(void)
{
//--- Il numero zero di righe nella matrice
ArrayResize(m_rows,0);
//---
}
//+--------------------------------------------------------------------------------+
//| Restituisce i valori della colonna sottoforma di CRow |
//+--------------------------------------------------------------------------------+
CRow CMatrix::GetColumnAsRow(const int col_index) const
{
//--- Una variabile per ottenere i valori della colonna
CRow row();
//--- Il numero di righe nella matrice
int rows=Rows();
//--- Se il numero di righe è maggiore di zero, esegue l'operazione
if(rows>0)
{
//--- Array per ricevere i valori della colonna con indice col_index
double array[];
ArrayResize(array,rows);
//--- Filling the array
for(int i=0;i<rows;i++)
{
//--- Controlla i numeri della collonna per la riga i - può eccedere i limiti dell'array
if(col_index>=this[i].Size())
{
Print(__FUNCSIG__,": Errore! Numero colonna ",col_index,"> grandezza riga ",i);
break; // la riga sarà un oggetto non inizializzato
}
array[i]=this[i][col_index];
}
//--- Crea una riga CRow basata sui valori dell'array
row=array;
}
//--- Risultato
return(row);
}
//+--------------------------------------------------------------------------------+
//| Addizione di due matrici |
//+--------------------------------------------------------------------------------+
CMatrix CMatrix::operator+(const CMatrix &m)
{
//--- Il numero di righe e colonne nella matrice passata
int cols=m.Cols();
int rows=m.Rows();
//--- La matrice per ricevere i risultati dell'addizione
CMatrix res(rows);
//--- Le grandezze della matrice devono corrispondere
if(cols!=Cols() || rows!=Rows())
{
//--- Addizione impossibile
Print(__FUNCSIG__,": Fallimento nell'aggiungere due matrici, le loro grandezze sono differenti");
return(res);
}
//--- Array ausiliario
double arr[];
ArrayResize(arr,cols);
//--- Va attraverso le righe da aggiungere
for(int i=0;i<rows;i++)
{
//--- Scrive i risultati dell'addizione delle stringe della matrice nell'array
for(int k=0;k<cols;k++)
{
arr[k]=this[i][k]+m[i][k];
}
//--- Piazza l'array nella riga della matrice
res[i]=arr;
}
//--- restituisce il risultato di addizione delle matrici
return(res);
}
//+--------------------------------------------------------------------------------+
//| Moltiplicazione di due matrici |
//+--------------------------------------------------------------------------------+
CMatrix CMatrix::operator*(const CMatrix &m)
{
//--- Numero di colonne della prima matrice, numero di righe passate nella matrice
int cols1=Cols();
int rows2=m.Rows();
int rows1=Rows();
int cols2=m.Cols();
//--- Matrice per ricevere i risultati dell'addizione
CMatrix res(rows1);
//--- Le matrici devono essere coordinate
if(cols1!=rows2)
{
//--- Moltiplicazione impossibile
Print(__FUNCSIG__,": Fallimento nel moltiplicare le due matrici, il formato è incompatibile"
"- il numero di colonne nel primo fattore dev'essere uguale al numero di righe nel secondo");
return(res);
}
//--- Array ausiliario
double arr[];
ArrayResize(arr,cols1);
//--- Riempie le righe nella moltiplicazione della matrice
for(int i=0;i<rows1;i++)// Va attraverso le righe
{
//--- Resetta l'array di ricezione
ArrayInitialize(arr,0);
//--- Va attraverso gli elementi nella riga
for(int k=0;k<cols1;k++)
{
//--- Prende i valori delle colonne k della matrice m nel for di CRow
CRow column=m.GetColumnAsRow(k);
//--- Moltiplica due righe e scrive i risultati della moltiplicazione scalare di vettori nell' i-esimo elemento
arr[k]=this[i]*column;
}
//--- piazza l'array arr[] nella i-esima riga della matrice
res[i]=arr;
}
//--- Restituisce il prodotto di due matrici
return(res);
}
//+--------------------------------------------------------------------------------+
//| Operazione di Assegnazione |
//+--------------------------------------------------------------------------------+
CMatrix *CMatrix::operator=(const CMatrix &m)
{
//--- Trova ed imposta il numero di righe
int rows=m.Rows();
ArrayResize(m_rows,rows);
//--- Riempiamo le righe con i valori di rows della matrice passata
for(int i=0;i<rows;i++) this[i]=m[i];
//---
return(GetPointer(this));
}
//+--------------------------------------------------------------------------------+
//| Rappresentazione stringa della matrice |
//+--------------------------------------------------------------------------------+
string CMatrix::String(void) const
{
string out="";
int rows=Rows();
//--- Forma stringa per stringa
for(int i=0;i<rows;i++)
{
out=out+this[i].String()+"\r\n";
}
//--- Risultato
return(out);
}
|
Vedi anche
L' overloading, Operazioni aritmetiche, Overloading di funzioni, Regole di precedenze