Polymorphismus

Polymorphismus ist die Moeglichkeit für Objekte verschiedener Klassen verbunden durch Vererbung beim Zugriff zu derselben Funktion-Element verschieden zu reagieren. Das hilft, universelle Mechanismen zu schaffen, die nicht nur das Verhalten der Basisklasse beschreiben, sondern auch das Verhalten der Klassen-Nachfolger.

Setzen wir die Ausarbeitung der Basisklasse CShape fort, in der wir die Funktion-Glied GetArea() definieren, die für Berechnung der Figurflaeche bestimmt ist. In allen Klassen-Nachfolger, erzeugt durch Vererbung von der Basisklasse, definieren wir die Funktion entsprechend den Regeln der Flaechenberechnung für konkrete Figur um.

für Quadrat (Klasse CSquare) wird die Flaeche durch Seiten, für Kreis (Klasse CCircle) durch Radius berechnet usw. Wir können Feld schaffen für Speichern von Objeketen des Typs CShape, in dem sowohl das Objekt der Basisklasse, als auch alle ihre Nachfolger gespeichert werden können. Im Nachhinein können wir dieselbe Funktion für jedes Element dieses Feldes aufrufen.  

Beispiel:

//--- Basisklasse
class CShape
  {
protected
   int            m_type;                // Figurtyp
   int            m_xpos;                // X - Koordinate des Bezugspunktes 
   int            m_ypos;                // Y - Koordinate des Bezugspunktes
public:
   void           CShape(){m_type=0;};   // Konstruktor, Typ ist Null gleich 
   int            GetType(){return(m_type);};// gibt Figurtyp zurück 
virtual
   double         GetArea(){return (0); }// gibt Figurflaeche zurück
  };

Jetzt haben alle Variablen die Funktion-Glied getArea(), die Nullwert zurückgibt. Realisierung dieser Funktion wird  in jedem Nachfolger verschieden sein.

//--- sekundaere Klasse Kreis
class CCircle : public CShape            // nach Doppelpunkt wird Basisklasse angegeben ,
  {                                      // von der Vererbung erfolgt  
private:
   int            m_radius;              // Kreisradius
 
public:
   void           CCircle(){m_type=1;};  // Konstruktor, Typ ist 1 gleich  
   void           SetRadius(double r){m_radius=r;};
   virtual double GetArea(){return (3.14*m_radius*m_radius);}// Kreisflaeche
  };

Für Quadrat sieht Klassenerklärung ähnlich aus:

//--- sekundaere Klasse Quadrat
class CSquare : public CShape            // nach Doppelpunkt wird Basisklasse angegeben,
  {                                      // von der Vererbung erfolgt  
private:
   int             m_square_side;        // Quadratseite
 
public:
   void            CSquare(){m_type=2;}; // Konstruktor, Typ ist 2 gleich
   void            SetSide(double s){m_square_side=s;};
   virtual double GetArea(){return (m_square_side*m_square_side);}//Quadratinhalt
  };

Da für Berechnung des Quadrats und des Kreises entsprechende Werte der Gliesder m_radius und m_square_side notwendig sind, haben wir in der Erklärung der entsprechenden Klassen Funktionen SetRadius und SetSide() zugesetzt.

Es wird davon ausgegangen, dass in unserem Programm Objekte der verschiedenen Typen (CCircle und CSquare) abgeleitet von einem Basistyp CShape verwendet werden. Polymorphismus erlaubt die Erstellung eines Arrays von Objekten der Basisklasse CShape, aber bei der Deklaration von diesem Array sind diese Objekte noch nicht bekannt und ihre Typ nicht definiert ist.

Die Entscheidung, welcher Typ von Objekt wird in jedem Element des Arrays enthalten sein werden direkt während der Programmausführung genommen werden. Dies beinhaltet die dynamische Erstellung von Objekten der entsprechenden Klassen, und damit die Notwendigkeit, Objekt Zeiger anstelle von Objekten zu verwenden..

Das new-Array ist für die dynamische Erzeugung von Objekten eingesetzt. Jedes solches Objekt muss einzeln und explizit mit dem Operator delete gelöscht werden. Deshalb erklären wir ein Array den Zeigern von CShape Typ, und erstellen wir ein Objekt einer richtigen Typ für jedes Element (new Klassname), wie im folgenden Skript Beispiel gezeigt:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Erklären wir Array von Zeigern auf Objekte der Basistyp 
   CShape *shapes[5];   // Array von Zeigern auf Objekte CShape
 
//--- Hier füllen wir das Array mit abgeleiteten Objekten
//--- Erklären wir Zeigern auf Objekt der CCircle-Typ
   CCircle *circle=new CCircle();
//--- Stellen wir die Eigenschaften von Objekt im circle Zeiger auf
   circle.SetRadius(2.5);
//--- Platzieren wir den Zeigerwert in shapes[0]
   shapes[0]=circle;
 
//--- Erstellen wir noch ein Objekt CCircle und schreiben sein Zeiger in shapes[1]
   circle=new CCircle();
   shapes[1]=circle;
   circle.SetRadius(5);
 
//--- Hier haben wir absichtlich "vergessen ", einen Wert für shapes[2] zu geben
//circle=new CCircle();
//circle.SetRadius(10);
//shapes[2]=circle;
 
//--- Setzen wir NULL für die nicht genutzte Element
   shapes[2]=NULL;
 
//--- Erstellen wir Objekt CSquare und schreiben sein Zeiger in shapes[3]
   CSquare *square=new CSquare();
   square.SetSide(5);
   shapes[3]=square;
 
//--- Erstellen wir CSquare und schreiben sein Zeiger in shapes[4]
   square=new CSquare();
   square.SetSide(10);
   shapes[4]=square;
 
//--- Es gibt ein Array von Zeiger, erhalten wir sein Größe
   int total=ArraySize(shapes);
//--- Passen wir in der Schleife über alle Zeiger in dem Array 
   for(int i=0; i<5;i++)
     {
      //--- Wenn der Zeiger an dem angegebenen Index gültig ist
      if(CheckPointer(shapes[i])!=POINTER_INVALID)
        {
         //--- Speichern wir Tap und Fläche der Figur in Logs
         PrintFormat("Objekt von Typ %d mit Fläche %G",
               shapes[i].GetType(),
               shapes[i].GetArea());
       ;}
      //--- Wenn der Zeiger ist vom Typ POINTER_INVALID
      else
        {
         //--- Melden wir einen Fehler
         PrintFormat("Objekt shapes[%d] ist nicht initialisiert! Sein Zeiger ist %s",
                     i,EnumToString(CheckPointer(shapes[i])));
        }
     }
 
//--- Wir müssen zerstören alle erstellten dynamischen Objekten
   for(int i=0;i<total;i++)
     {
      //--- Wir können nur die Objekte die den Zeiger vom typ POINTER_DYNAMIC haben löschen 
      if(CheckPointer(shapes[i])==POINTER_DYNAMIC)
        {
         //--- Melden wir die Löschung
         PrintFormat("Löschen wir shapes[%d]",i);
         //--- Löschen wir ein Objekt durch seinen Zeiger
         delete shapes[i];
        }
     }
  }

Beachten Sie, dass wenn das Object mit dem Operator delete gelöscht wird, der Typ seinen Zeiger überprüfen werden soll. Nur Objekte mit dem Zeiger POINTER_DYNAMIC kann durch delete gelöscht werden. Für andere Typen wird ein Fehler zurückgegeben.

Ausser Umdefinieren der Funktion bei der Vererbung schließt Polymorphismus auch Realisierung derselben Funktion mit verschiedenem Parametervorrat innerhalb einer Klasse. Das bedeutet in der Klasse können mehrere Funktionen mit demselben Namen, aber verschiedenem Parametertyp/Parametervorrat definiert werden. In diesem Fall wird Polymorphismus durch Funktionüberladen realisiert.

Sehen Sie auch

Standardbibliothek