Überladen von Operatoren

Zur leichteren Lesen und Schreiben von Code ist Überladen einiger Operationen erlaubt. Ein Überladenoperator wird mit dem Schlüsselwort Betreiber geschrieben. Die folgenden Operatoren können überladen werden:

  • binäre +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^;
  • unäre +,-,++,--,!,~;
  • Zuweisungsoperator =;
  • Indizierungsoperator [].

 

Überladen von Operatoren ermöglicht den Einsatz des Betriebssystems Notation (geschrieben in der Form von einfachen Ausdrücken) zu komplexen Objekte - Strukturen und Klassen. Schreiben von Ausdrücken mit überladenen Operationen vereinfacht den Blick auf den Quellcode, weil eine komplexere Implementierung verborgen ist.

Betrachten wir zum Beispiel komplexe Zahlen, die aus Real- und Imaginärteil bestehen. Sie sind weit verbreitet in der Mathematik weit verbreitet. Die MQL5 Sprache hat keinen Datentyp für Darstellung der komplexen Zahlen, aber es ist möglich, einen neuen Datentyp in Form einer Struktur oder Klasse zu erstellen. Wir deklarieren die Struktur complex und definieren vier Methoden in dieser Struktur, die die vier arithmetischen Operationen implementieren:

//+------------------------------------------------------------------+
//| Struktur für Operationen mit komplexen Zahlen                    |
//+------------------------------------------------------------------+
struct complex
  {
   double            re; // Realteil
   double            im; // Imaginärteil
   //--- Konstruktoren
                     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) { }
   //--- Rechenoperationen
   complex           Add(const complex &l,const complex &r) const;  // Addition
   complex           Sub(const complex &l,const complex &r) const;  // Subtraktion
   complex           Mul(const complex &l,const complex &r) const;  // Multiplikation
   complex           Div(const complex &l,const complex &r) const;  // Division
  };

Jetzt können wir Variablen, die komplexen Zahlen vertreten, in unserem Code deklarieren und mit ihnen arbeiten.

Zum Beispiel:

void OnStart()
  {
//--- Deklarieren und initialisieren Variablen eines komplexen Typs
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Addition zweier Zahlen
   complex z;
   z=a.Add(a,b);
   PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Multiplikation zweier Zahlen
   z=a.Mul(a,b);
   PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Division zweier Zahlen
   z=a.Div(a,b);
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
  }

Aber es wäre bequemer für die üblichen arithmetischen Operationen mit komplexen Zahlen die üblichen Operatoren "+", "-", "*" und "/" zu verwenden.

Das Schlüsselwort operator wird verwendet um die Memberfunktion, die eine Typkonvertierung führt, zu definieren. Unäre und binäre Operationen für Variable-Klassenobjekte können als nicht-statische Memberfunktionen überladen werden. Sie implizit auf das Objekt der Klasse wirken.

Die meisten binären Operatoren können wie normale Funktionen, die eine oder beide Argumente als Variable der Klasse oder als ein Zeiger auf ein Objekt dieser Klasse nehmen, überladen werden. Für unseren Typ complex wird Überladen im Deklarieren wie folgt aussehen:

   //--- Operatoren
   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)); }

Ein komplettes Beispiel des Skripts:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- deklarieren und initialisieren Variablen eines komplexen Typs
   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;
//--- addieren zwei Zahlen
   complex z=a+b;
   PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- Multiplikation zweier Zahlen
 
   z=a*b;
   PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- Division zweier Zahlen
   z=a/b;
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
  }
//+------------------------------------------------------------------+
//| eine Struktur für Operationen mit komplexen Zahlen               |
//+------------------------------------------------------------------+
struct complex
  {
   double            re; // Realteil
   double            im; // Imaginärteil
   //--- Konstruktoren
                     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) { }
   //--- arithmetische Operationen
   complex           Add(const complex &l,const complex &r) const;  // Addition
   complex           Sub(const complex &l,const complex &r) const;  // Subtraktion
   complex           Mul(const complex &l,const complex &r) const;  // Multiplikation
   complex           Div(const complex &l,const complex &r) const;  // Division
   //--- binäre Operatoren
   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)); }
  };
//+------------------------------------------------------------------+
//| Addition                                                         |
//+------------------------------------------------------------------+
complex complex::Add(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re+r.re;
   res.im=l.im+r.im;
//--- Ergebnis
   return res;
  }
//+------------------------------------------------------------------+
//| Subtraktion                                                      |
//+------------------------------------------------------------------+
complex complex::Sub(const complex &l,const complex &r) const
  {
   complex res;
//---
   res.re=l.re-r.re;
   res.im=l.im-r.im;
//--- Ergebnis
   return res;
  }
//+------------------------------------------------------------------+
//| Multiplikation                                                   |
//+------------------------------------------------------------------+
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;
//--- Ergebnis
   return res;
  }
//+------------------------------------------------------------------+
//| Division                                                         |
//+------------------------------------------------------------------+
complex complex::Div(const complex &l,const complex &r) const
  {
//--- leere komplexe Zahl
   complex res(EMPTY_VALUE,EMPTY_VALUE);
//--- prüfen zu Null
   if(r.re==0 && r.im==0)
     {
      Print(__FUNCTION__+": number is zero");
      return(res);
     }
//--- Hilfsvariable 
   double e;
   double f;
//--- Wahl der Rechenvariante
   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;
     }
//--- Ergebnis
   return res;
  }

 

Die meisten unären Operationen für Klassen können als die üblichen Funktionen, die ein einziges Argument-Klassenobjekt oder einen Zeiger auf es nehmen, überladen werden. Fügen wir eine Überladen unäre Operationen "-" und "!".

//+------------------------------------------------------------------+
//| Eine Struktur für Operationen mit komplexen Zahlen               |
//+------------------------------------------------------------------+
struct complex
  {
   double            re;       // Realteil
   double            im;       // Imaginärteil
...
   //--- unäre Operatoren 
   complex operator-()  const// unäres Minus
   bool    operator!()  const// Negation
  };
...
//+------------------------------------------------------------------+
//| Überladen von Operator "unäres Minus"                            |
//+------------------------------------------------------------------+
complex complex::operator-() const
  {
   complex res;
//---
   res.re=-re;
   res.im=-im;
//--- Ergebnis
   return res;
  }
//+------------------------------------------------------------------+
//| Überladen von Operator "logische Negation"                       |
//+------------------------------------------------------------------+
bool complex::operator!() const
  {
//--- Sind die realen und imaginären Teile der komplexen Zahl gleich Null?
   return (re!=0 && im!=0);
  }

 

Jetzt können wir den Wert einer komplexen Zahl durch Null prüfen und einen negativen Wert erhalten:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- deklarieren und initialisieren Variablen eines komplexen Typs
   complex a(2,4),b(-4,-2);
   PrintFormat("a=%.2f+i*%.2f,   b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- Division zweier Zahlen z=a/b;
   complex z=a/b;
   PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//--- Eine komplexe Zahl wird standardmäßig Null gleich (im Standardkonstruktor re==0 und im==0)
   complex zero;
   Print("!zero=",!zero);
//--- einen negativen Wert anweisen
   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);
//--- prüfen wir zu Null noch einmal   
   Print("!zero=",!zero);
//---
  }

Bitte beachten Sie, dass wir nicht in diesem Fall den Zuweisungsoperator "=" überladen haben, wie Strukturen von einfachen Typen können eins in andere direkt kopiert werden. So können wir nun den Code für Berechnungen mit komplexen Zahlen in der üblichen Weise schreiben.

Überladen von Index-Operator ermöglicht Erhalten von im Objekt eingeschlossenen Arrays auf einfache und gewohnte Art, und dies trägt auch zu einem besseren Verständnis und Lesbarkeit des Quellcodes. Zum Beispiel müssen wir den Zugang zu einem Symbol im String an der angegebenen Position vorsehen. Ein String in der Sprache MQL5 ist ein eigener Typ string, der nicht ein Array von Symbole ist, sondern mit Hilfe einer überladenen Index-Operation in der generierten Klasse CString können wir eine einfache und transparente Arbeit stellen:

//+------------------------------------------------------------------+
//| Klasse, um Symbole im String als Array in Symbole zu zugreifen   |
//+------------------------------------------------------------------+
class CString
  {
   string            m_string;
  
public:
                     CString(string str=NULL):m_string(str) { }
   ushort operator[] (int x) { return(StringGetCharacter(m_string,x)); }
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()  
  {
//--- Array für Erhalten der Symbole aus einem String
   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;
//--- Bilden einen Satz mit Symbolen aus der Variable str
   for(int i=0,n=ArraySize(x);i<n;i++)
     {
      res+=ShortToString(str[x[i]]);
     }
//--- Ausgabe von Ergebnisse
   Print(res);
  }

 

Ein weiteres Beispiel für ein Überladen der Index-Operation ist Arbeit mit Matrizen. Die Matrix ist ein zweidimensionales dynamisches Array. Größe des Arrays sind nicht im Voraus definiert. So kann man nicht ein Array von Form array[][] ohne Angabe der Größe der zweiten Dimension deklarieren, und dann dieses Array als Parameter übergeben. Eine mögliche Lösung ist eine spezielle Klasse CMatrix, die ein Array von Objekten der Klasse CRow enthält.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Operationen der Addition und Multiplikation von Matrizen
   CMatrix A(3),B(3),C();
//--- bereiten die Arrays für Strings
   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};
//--- füllen die Matrizen
   A[0]=a1; A[1]=a2; A[2]=a3;
   B[0]=b1; B[1]=b2; B[2]=b3;
//--- Ausgabe von Matrizen in "Experts" Journal
   Print("---- Elemente der Matrix A");
   Print(A.String());
   Print("---- Elemente der Matrix B");
   Print(B.String());
 
//--- Multiplikation von Matrizen
   Print("---- Multiplikation von Matrizen A und B");
   C=A+B;
//--- Ausgabe der formatierten String-Darstellung
   Print(C.String());
 
//---Multiplikation von Matrizen
   Print("---- Produkt von Matrizen A und B");
   C=A*B;
   Print(C.String());
 
//--- jetzt zeigen wir wie Werte im Stil des dynamischen Arrays matrix[i][j] zu erhalten
   Print("die Werte der Matrix C elementweise ausgeben");
//--- Durchschleifen der Strings der Matrix - Objekte CRow
   for(int i=0;i<3;i++)
     {
      string com="| ";
      //--- bilden Strings aus der Matrix für den Wert
      for(int j=0;j<3;j++)
        {
         //--- erhalten ein Element der Matrix aus Nummer von Zeile und Spalte
         double element=C[i][j];// [i] - Zugriff zu CRow im Array m_rows[] ,
                                // [j] - Überladener Index-Operator in CRow
         com=com+StringFormat("a(%d,%d)=%G ; ",i,j,element);
        }
      com+="|";
      //--- Die Werte von der Zeile ausgeben
      Print(com);
     }
  }
//+------------------------------------------------------------------+
//| Klasse "Zeile"                                                   |
//+------------------------------------------------------------------+
class CRow
  {
private:
   double            m_array[];
public:
   //--- Konstruktoren und Destruktor
                     CRow(void)          { ArrayResize(m_array,0);    }
                     CRow(const CRow &r) { this=r;                    }
                     CRow(const double &array[]);
                    ~CRow(void){};
   //--- Anzahl der Elementen in der Zeile
   int               Size(voidconst    { return(ArraySize(m_array));}
   //--- Gibt den String mit Werte zurück  
   string            String(voidconst;
   //--- Index-Operator
   double            operator[](int i) const  { return(m_array[i]);   }
   //--- Zuweisungsoperatoren
   void              operator=(const double  &array[]); // Array
   void              operator=(const CRow & r);         // ein anderes Objekt CRow
   double            operator*(const CRow &o);          // Objekt CRow für Multiplikation
  };
//+------------------------------------------------------------------+
//| Konstruktor zur Initialisierung einer Zeile mit einem Array      |
//+------------------------------------------------------------------+
void  CRow::CRow(const double &array[])
  {
   int size=ArraySize(array);
//--- Wenn das Array nicht leer ist
   if(size>0)
     {
      ArrayResize(m_array,size);
      //--- Füllen mit Arrays
      for(int i=0;i<size;i++)
         m_array[i]=array[i];
     }
//---
  }
//+------------------------------------------------------------------+
//| Zuweisungsoperation für das Array                                |
//+------------------------------------------------------------------+
void CRow::operator=(const double &array[])
  {
   int size=ArraySize(array);
   if(size==0) return;
//--- füllen das Array mit Werten
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=array[i];
//--- 
  }
//+------------------------------------------------------------------+
//| Zuweisungsoperation für CRow                                     |
//+------------------------------------------------------------------+
void CRow::operator=(const CRow  &r)
  {
   int size=r.Size();
   if(size==0) return;
//--- füllen das Array mit Werten
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=r[i];
//--- 
  }
//+------------------------------------------------------------------+
//| Operator der Multiplikation auf einer anderen Zeile              |
//+------------------------------------------------------------------+
double CRow::operator*(const CRow &o)
  {
   double res=0;
//--- Prüfung
   int size=Size();
   if(size!=o.Size() || size==0)
     {
      Print(__FUNCSIG__,": Fehler bei der Multiplikation zweier Matrizen, Die Größe sind nicht gleich");
      return(res);
     }
//--- multiplizieren die Arrays elementweise und addieren die Produkte
   for(int i=0;i<size;i++)
      res+=m_array[i]*o[i];
//--- Ereignis
   return(res);
  }
//+------------------------------------------------------------------+
//| Gibt die formatierte String-Darstellung zurück                   |
//+------------------------------------------------------------------+
string CRow::String(voidconst
  {
   string out="";
//--- wenn die Größe des Arrays mehr als Null ist
   int size=ArraySize(m_array);
//--- arbeiten nur mir Nicht-Null Anzahl der Elemente im Array
   if(size>0)
     {
      out="{";
      for(int i=0;i<size;i++)
        {
         //--- sammeln Werte in einen String
         out+=StringFormat(" %G;",m_array[i]);
        }
      out+=" }";
     }
//--- Ergebnis
   return(out);
  }
//+------------------------------------------------------------------+
//| Klasse "Matrix"                                                  |
//+------------------------------------------------------------------+
class CMatrix
  {
private:
   CRow              m_rows[];
 
public:
   //--- Konstruktoren und Destruktor
                     CMatrix(void);
                     CMatrix(int rows)  { ArrayResize(m_rows,rows);             }
                    ~CMatrix(void){};
   //--- Erhalten von Größen der Matrix
   int               Rows()       const { return(ArraySize(m_rows));            }
   int               Cols()       const { return(Rows()>0? m_rows[0].Size():0); }
   //--- gibt den Wert der Spalte als String CRow zurück
   CRow              GetColumnAsRow(const int col_index) const;
   //--- gibt einen String mit den Werten der Matrix zurück  
   string            String(voidconst;
   //--- Index-Operator gibt eine Zeile auf ihrer Nummer zurück
   CRow *operator[](int i) const        { return(GetPointer(m_rows[i]));        }
   //--- Additionoperator
   CMatrix           operator+(const CMatrix &m);
   //--- Multiplikationsoperator
   CMatrix           operator*(const CMatrix &m);
   //--- Zuweisungsoperator
   CMatrix          *operator=(const CMatrix &m);
  };
//+------------------------------------------------------------------+
//| Standardkonstruktor, erstellt Array von Strings mit Null-Größe   |
//+------------------------------------------------------------------+
CMatrix::CMatrix(void)
  {
//--- Null Zeilen in der Matrix  
   ArrayResize(m_rows,0);
//---  
  }
//+------------------------------------------------------------------+
//| Gibt den Wert der Spalte als String CRow zurück                  |
//+------------------------------------------------------------------+
CRow  CMatrix::GetColumnAsRow(const int col_index) const
  {
//--- Variable, um die Werte aus Spalte zu erhalten
   CRow row();
//--- Anzahl der Zeile in der Matrix
   int rows=Rows();
//--- Wenn die Anzahl der Zeilen größer als Null ist, führen die Operation
   if(rows>0)
     {
      //--- Array für Erhalten von Werte der Spalte mit Index col_index
      double array[];
      ArrayResize(array,rows);
      //--- füllen das Array
      for(int i=0;i<rows;i++)
        {
         //--- Prüfung der Spaltennummer für Zeile i, ob es über die Grenzen des Arrays überschritt
         if(col_index>=this[i].Size())
           {
            Print(__FUNCSIG__,": Fehler! Nummer der Spalte ",col_index,"> Größe der Zeile ",i);
            break// row bleibt ein nicht initialisiertes Objekt
           }
         array[i]=this[i][col_index];
        }
      //--- erstellen wir eine Zeile CRow basierend auf den Werten des Arrays 
      row=array;
     }
//--- Ergebnis
   return(row);
  }
//+------------------------------------------------------------------+
//| Multiplikation zweier Matrizen                                   |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator+(const CMatrix &m)
  {
//--- die Anzahl der Zeilen und Spalten in der übergebene Matrix
   int cols=m.Cols();
   int rows=m.Rows();
//--- Matrix, um das Ergebnis der Addition zu erhalten 
   CMatrix res(rows);
//--- Dimensionen der Matrix müssen identisch sein
   if(cols!=Cols() || rows!=Rows())
     {
      //--- kann nicht addieren
      Print(__FUNCSIG__,": Fehler bei der Addition zweier Matrizen, die Größe sind nicht gleich");
      return(res);
     }
//--- Hilfsarray
   double arr[];
   ArrayResize(arr,cols);
//--- Iteration durch die Zeilen für Addition
   for(int i=0;i<rows;i++)
     {
      //--- wir schreiben das Ergebnis der Addition von Zeilen der Matrix in ein Array
      for(int k=0;k<cols;k++)
        {
         arr[k]=this[i][k]+m[i][k];
        }
      //--- stellen das Array in der Zeile der Matrix
      res[i]=arr;
     }
//--- Ergebnis der Addition zweier Matrizes zurückgeben
   return(res);
  }
//+------------------------------------------------------------------+
//| Multiplikation zweier Matrizen                                   |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator*(const CMatrix &m)
  {
//--- Anzahl der Spalten der ersten Matrix Anzahl der Zeilen in der übergebenen Matrix
   int cols1=Cols();
   int rows2=m.Rows();
   int rows1=Rows();
   int cols2=m.Cols();
//--- Matrix, um das Ergebnis der Addition zu erhalten 
   CMatrix res(rows1);
//--- Matrizes sollten abgestimmt werden
   if(cols1!=rows2)
     {
      //--- Kann nicht multiplizieren
      Print(__FUNCSIG__,": Fehler bei der Multiplikation zweier Matrizen, ist das Format nicht kompatibel "
            "- Anzahl der Spalten in den ersten Faktor sollte gleich der Anzahl der Zeilen in der zweiten sein");
      return(res);
     }
//--- Hilfsarray
   double arr[];
   ArrayResize(arr,cols1);
//--- füllen Zeilen in der Matrix von Multiplikation
   for(int i=0;i<rows1;i++)// iterieren durch die Zeilen
     {
      //--- Array-Empfänger auf Null setzen
      ArrayInitialize(arr,0);
      //--- iterieren Elemente in der Zeile
      for(int k=0;k<cols1;k++)
        {
         //--- Nehmen Werte der Spalte k der Matrix m als ein String CRow
         CRow column=m.GetColumnAsRow(k);
         //--- multiplizieren die beiden Zeilen und schreibt das Ergebnis der Skalarmultiplikation von Vektoren in der i-te Element
         arr[k]=this[i]*column;
        }
      //--- setzen Array arr[] in der Zeile i der Matrix
      res[i]=arr;
     }
//--- Produkt zweier Matrizen zurückgeben
   return(res);
  }
//+------------------------------------------------------------------+
//| Zuweisungsoperation                                              |
//+------------------------------------------------------------------+
CMatrix *CMatrix::operator=(const CMatrix &m)
  {
//--- finden und definieren die Anzahl der Zeilen
   int rows=m.Rows();
   ArrayResize(m_rows,rows);
//--- füllen unsere Strings mit den Werten der Zeilen der übergebenen Matrix
   for(int i=0;i<rows;i++) this[i]=m[i];
//---
   return(GetPointer(this));
  }
//+------------------------------------------------------------------+
//| Eine String-Darstellung der Matrix                               |
//+------------------------------------------------------------------+
string CMatrix::String(voidconst
  {
   string out="";
   int rows=Rows();
//--- bilden Zeile für Zeile
   for(int i=0;i<rows;i++)
     {
      out=out+this[i].String()+"\r\n";
     }
//--- Ergebnis
   return(out);
  }

Sehen Sie auch

Überladen, Arithmetische Operationen, Überladen von Funktionen, Prioritäten und die Reihenfolge der Operationen