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(voidconst    { return(ArraySize(m_array));}
   //--- Restituisce la stringa con valori 
   string            String(voidconst;
   //--- 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(voidconst
  {
   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(voidconst;
   //--- 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(voidconst
  {
   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