Vererbung

Eigenartigkeit von OOP besteht in der Aufmunterung zu Wiederverwendung des Kodes mittels Vererbung. Die neue Klasse wird von der vorausgehenden erzeugt, die Basisklasse genannt wird. Sekundäre Klasse verwendet die Glieder der Basisklasse, kann aber sie verändern und erweitern.

Viele Typen sind Variationen der vorhandenen Typen. Oft ist es ermüdend einen neuen Koden für jeden auszuarbeiten. Außerdem erscheinen mit neuem Kode neue Fehler.  Sekundäre Klasse erbt die Beschreibung der Basisklasse und macht es unnötig, Kode wieder auszuarbeiten und zu testen. Vererbungverhalten sind hierarchisch.  

Hierarchie ist die Methode, die Elemente in in voller Vielfältigkeit und Kompliziertheit zu kopieren ermöglicht. Sie leitet Objektklassifizierung ein. ZB gibt es im periodischen System von Elementen Gase. Sie haben Eigenschaften, die allen Systemelementen eigen sind.

Neutrale Gase stellen die nächste wichtige Subklasse dar. Hierarchie besteht darin, dass neutrales Gas, zB. Argon ist ein Gas, und Gas ist ein Systemelement. Solche Hierarchie ermöglicht das Verhalten von neutralen Gasen leicht zu interpretieren. Wir wissen, dass ihre Atome Protonen und Elektronen haben, was für andere Elemente auch typisch ist.

Wit wissen, dass sie beim Raumtemperatur gasförmig sind, wie alle Gase. Wir wissen, dass kein Gas aus der Subklasse neutrale Gase mit anderen Elementen reagiert und das ist die Eigenschaft aller neutralen Gase.  

Betrachten wir Vererbung am Beispiel mit geometrischen Figuren. Für Beschreibung aller Vielfältigkeit von einfachen Figuren (Kreis, Dreieck, Rechteck, Quadrat usw.) ist es am besten eine Basisklasse zu erzeugen (ADT), die Vorfahr aller sekundären Klassen ist.

Erzeugen wir eine Basisklasse CShape, in der es nur allgemeine Glieder gibt, die die Figur beschreiben. Diese Glieder beschreiben die Eigenschaften, die jeder Figur eigen sind  - Figurtyp und Koordinaten des Bezugspunktes.  

Beispiel:

//--- Basisklasse Figur
class CShape
  {
protected:
   int       m_type;                   // Figurtyp
   int       m_xpos;                   // X - Koordinate des Bezugspunktes 
   int       m_ypos;                   // Y - Koordinate des Bezugspunktes
public:
             CShape(){m_type=0; m_xpos=0; m_ypos=0;} // Konstruktor
   void      SetXPos(int x){m_xpos=x;} // stellen wir X ein
   void      SetYPos(int y){m_ypos=y;} // stellen wir Y ein
  };

Weiter erzeugen wir von der Basisklasse sekundäre Klassen, in die wir notwendige Felder zufügen, die jede konkrete Klasse präzisieren. Für die Figur Circle(Kreis) ist es notwendig das Glied zuzufügen, das Radiuswert enthält. Figur Quadrate (Quadrat) ist durch den Wert der Quadratseite charakterisiert. Darum werden sekundäre Klassen, die von der Basisklasse CShape geerbt werden, folgenderweise erklärt :

//--- sekundaere Klasse Kreis
class CCircle : public CShape        // nach Doppelpunkt wird Basisklasse angegeben,
  {                                    // von der Vererbung erfolgt  
private:
   int             m_radius;           // Kreisradius
 
public:
                   CCircle(){m_type=1;}// Konstruktor, Typ ist 1 gleich
  };

für Quadrat sieht Klassenerklärung ähnlicherweise aus:

//--- sekundaere Klasse Quadrat
class CSquare : public CShape        // nach Doppelpunkt wird Basisklasse angegeben ,
  {                                    // von der Vererbung erfolgt  
private:
   int            m_square_side;       // Quadratseite 
 
public:
                  CSquare(){m_type=2;} // Konstruktor ist 2 gleich
  };

Es muss bemerkt werden, dass bei der Ezeugung eines Objektes vor allem Konstruktor einer Basisklasse, und dannKonstruktor einer sekundären Klasse aufgerufen wird.  Bei der Entfernung eines Objektes wird zuerst Destruktor einer sekundären Klasse und dann Destruktor einer Basisklasse aufgerufen.  

So nach der Erklärung allgemeiner Glieder in einer Basisklasse können wir in sekundäre Klassen zusätzliche Glieder hinzufügen, die eine konkrete Klasse präzisieren. Vererbung erlaubt große Kodebibliotheken zu erzeugen, die mehrmals und wiederholt verwendet werden können.

Syntax der Erzeugung der sekundären Klasse von der schon vorhandenen sieht so aus:

class Klassenname : 
          (public | protected | privateopt  Name der Basisklasse
  {                                    
    Gliedererklärungen
  };

Einer der Aspekte der sekundären Klasse ist Sichtbarkeit (Offenheit) seiner Glieder-Nachfolger. Schlüsselwörter public, protected und private werden verwendet, um anzudeuten, inwieweit Glieder der Basisklasse für Glieder der sekundären Klasse zugänglich sind. Verwendung im Namen der sekundären Klasse der Schlüsselklasse public, der nach Doppelpunkt folgt, bedeutet, dass geschützte und offene (protected und public) Glieder der Basisklasse CShape wie geschützte und offene Glieder der sekundären Klasse CCircle geerbt werden müssen.  

Geschlossene Glieder der Basisklasse sind für sekundäre Klasse nicht zugänglich. Offene Vererbung bedeutet ach, dass sekundäre Klassen (CCircle und CSquare)  CShape sind. D.h. Quadrat (CSquare) ist eine Figur (CShape), eine Figur mus aber nicht unbedingt ein Quadrat sein.

Sekundäre Klasse ist Modifikation der Basisklasse; sie erbt geschützte und offene Glieder der Basisklasse. Erst Konstruktors und Destruktors der Basisklasse können nicht geerbt werden.  Oft werden in die sekundäre Klasse neue Glieder zu Gliedern der Basisklasse hinzugefügt.  

Sekundäre Klasse kann Realisierung der Funktionen-Glieder einschließen, die sich von der Basisklasse unterscheidet. Das hat nichts mit Überladung zu tun, wenn der Sinn desselben Funktionsnamens verschieden für verschiedene Signaturen sein kann.  

Beim geschützten Vererbung werden offene und geschützte Glieder der Basisklasse zu offenen und geschützten Gliedern der sekundären Klasse. Beim geschlossenen Vererbung werden offene und geschützte Glieder de Basisklasse zu geschlossenen Gliedern der sekundären Klasse.

Beim geschützten und geschlossenen Vererbung stimmt es nicht, dass Objekt der sekundären Klasse Objekt der Basisklasse ist. Geschütztes und geschlossenes Vererbung treten selten auf und müssen sehr vorsichtig verwendet werden.

Es sollte klar sein, dass die Art der Vererbung (public, protected oder private) beeinflusst nicht die Mittel für den Zugang an die Glieder der Basisklassen in der Vererbungshierarchie der abgeleiteten Klasse (Erben). In jeder Art der Vererbung, nur Glieder einer Basisklasse, die mit dem Zugang-Spezifizierer public und protected deklariert worden, sind aus abgeleiteten Klassen erreichbar. Betrachten wir das obige Beispiel:

#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Beispielklasse mit mehreren Arten des Zugriffs                   |
//+------------------------------------------------------------------+
class CBaseClass
  {
private:             //--- Privater Mitglied ist von den abgeleiteten Klassen nicht zugänglich
   int               m_member;
protected:           //--- Geschützter Mitglied ist von der Basisklasse und ihren abgeleiteten Klassen zugänglich
   int               Member(){return(m_member);}
public:              // Konstruktor der Klasse ist allen zugänglich
                     CBaseClass(){m_member=5;return;};
private:             // Private Methode für die Zuweisung eines Wertes an den Mitglied m_member
   void              Member(int value) { m_member=value;};
 
  };
//+------------------------------------------------------------------+
//| Die abgeleitete Klasse mit Fehlern                               |
//+------------------------------------------------------------------+
class CDerivaed: public CBaseClass // Es ist nicht notwendig public-Vererbung anzugeben, sie ist standardmäßig
  {
public:
   void Func() // In der abgeleiteten Klasse definieren wir eine Funktion mit Aufrufen an Basisklassenmitglieder 
     {
      //--- Versuch private Mitglied der Basisklasse zu ändern
      m_member=0;        // Fehler, ist ein privates Mitglied der Basisklasse nicht zugänglich
      Member(0);         // Fehler, ist ein privates Mitglied der Basisklasse in der abgeleiteten Klasse nicht zugänglich
      //--- Lesen einer Basisklassenmitglied
      Print(m_member);   // Fehler, ist ein privates Mitglied der Basisklasse nicht zugänglich
      Print(Member());   // Kein Fehler, offenes Mitglied der Basisklasse ist immer zugänglich
     }
  };

In diesem Beispiel hat Klasse CBaseClass nur eine öffentliche Methode - den Konstruktor. Konstruktoren sind automatisch aufgerufen, wenn ein Objekt der Klasse erstellt wird. So kann man nicht von außen den privaten Mitglieder m_member und die geschützte Methode Member() aufrufen. Aber mit einer offenen (public) Vererbung ist die Methode der Basisklasse Member() von den abgeleiteten Klassen zugänglich.

In der geschützte (protected) Vererbung, werden alle Basisklassenmitglieder mit einem offenen und geschützten Zugriff geschützt. Dies bedeutet, dass, wenn das öffentliche Datenelemente und Methoden der Basisklasse von außen zugänglich sind, dann in geschützter Vererbung sind sie nur aus den Klassen des Kindes und seiner anschließenden abgeleiteten Klassen zugänglich.

//+------------------------------------------------------------------+
//| Beispielklasse mit mehreren Arten des Zugriffs                   |
//+------------------------------------------------------------------+
class CBaseMathClass
  {
private:             //--- Privater Mitglied ist von den abgeleiteten Klassen nicht zugänglich
   double            m_Pi;
public:              //--- Erhalten und angeben Wert für m_Pi
   void              SetPI(double v){m_Pi=v;return;};
   double            GetPI(){return m_Pi;};
public:              // Konstruktor der Klasse ist allen zugänglich
                     CBaseMathClass() {SetPI(3.14);  PrintFormat("%s",__FUNCTION__);};
  };
//+------------------------------------------------------------------+
//| Abgeleitete Klasse, in deren m_Pi kann nicht verändert werden    |
//+------------------------------------------------------------------+
class CProtectedChildClass: protected CBaseMathClass // Geschützte Vererbung
  {
private:
   double            m_radius;
public:              //--- Offene Methoden in der abgeleiteten Klasse
   void              SetRadius(double r){m_radius=r; return;};
   double            GetCircleLength(){return GetPI()*m_radius;};
  };
//+------------------------------------------------------------------+
//| Funktion started den Skript                                      |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Bei der Erstellung des Kindes wird Basisklassenkonstruktor automatisch aufgerufen
   CProtectedChildClass pt;
//--- Radius angeben
   pt.SetRadius(10);
   PrintFormat("Length=%G",pt.GetCircleLength());
//--- Wenn den folgenden String benutzen, bekommen wir eine Fehlermeldung beim Kompilierung, da SetPi() geschützt ist
// pt.SetPI(3); 
 
//--- und jetzt deklarieren wir eine Variable einer Basisklasse und versuchen, eine Konstante Pi bis 10 zu definieren
   CBaseMathClass bc;
   bc.SetPI(10);
//--- Ergebnis
   PrintFormat("bc.GetPI()=%G",bc.GetPI());
  }

Dieses Beispiel zeigt, dass die Methoden SetPI() und GetPi() in der Basisklasse CBaseMathClass offen sind und von überall angerufen werden können. Aber zur gleichen Zeit für sein Kind CProtectedChildClass können die Aufrufe dieser Methoden nur aus Methoden der Klasse CProtectedChildClass oder ihren Kinder gemacht werden.

Mit einer privaten Vererbung werden alle Mitglieder der Basisklasse mit Zugriff public und protected geschlossen, und mit weiterer Vererbung ist Aufruf dieser Mitglieder unmöglich.

In MQL5 gibt es kein multiple Vererbung.

Sehen Sie auch

Strukturen und Klassen