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