Ereditarietà

La caratteristica della OOP è l'incoraggiamento del riutilizzo del codice attraverso l'ereditarietà. Una nuova classe viene costituita da una esistente, che è chiamata la classe base. La classe derivata utilizza i membri della classe base, ma può anche modificarli e completarli.

Molti tipi sono variazioni dei tipi esistenti. È spesso tediante sviluppare un nuovo codice per ciascuno di essi. Inoltre, un nuovo codice comporta nuovi errori. La classe derivata eredita la descrizione della classe base, in tal modo qualsiasi nuovo sviluppo e re-testing del codice non è necessario. Le relazioni di ereditarietà sono gerarchiche.

La gerarchia è un metodo che permette di copiare gli elementi in tutta la loro diversità e complessità. Esso introduce la classificazione oggetti. Ad esempio, la tavola periodica degli elementi ha i gas. Essi possiedono le proprietà intrinseche a tutti gli elementi periodici.

Gas inerti costituiscono la successiva sottoclasse più importante. La gerarchia è che il gas inerte, come l'argon, è un gas, ed il gas, a sua volta, è parte del sistema. Tale gerarchia permette di interpretare il comportamento di gas inerti facilmente. Sappiamo che i loro atomi contengono protoni ed elettroni, che è vero per tutti gli altri elementi.

Sappiamo che sono in uno stato gassoso a temperatura ambiente, come tutti i gas. Sappiamo che nessun gas della sottoclasse dei gas inerti, entra usualmente in reazione chimica con altri elementi, ed è una proprietà di tutti i gas inerti.

Si consideri un esempio della successione di forme geometriche. Per descrivere la varietà di forme semplici (cerchio, triangolo, rettangolo, quadrato, ecc.), il modo migliore è quello di creare una classe base (ADT), che è l'antenato di tutte le classi derivate.

Creiamo un classe base CShape, che contiene solo i componenti più comuni che descrivono la forma. Questi membri descrivono proprietà caratteristiche a qualsiasi forma - il tipo di forma e le principali coordinate di punto di ancoraggio.

Esempio:

//--- La classe base Shape
class CShape
  {
protected:
   int       m_type;                   // tipo di Shape(forma)
   int       m_xpos;                   // X - coordinate del punto base
   int       m_ypos;                   // Y - coordinate del punto base
public:
             CShape(){m_type=0; m_xpos=0; m_ypos=0;} // costruttore
   void      SetXPos(int x){m_xpos=x;} // imposto X
   void      SetYPos(int y){m_ypos=y;} // imposto Y
  };

Successivamente, creare nuove classi derivate dalla classe base, in cui si andranno ad aggiungere i campi necessari, ognuna delle quali specifica una certa classe. Per la forma del Cerchio è necessario aggiungere un membro che contiene il valore del raggio. La forma del Quadrato è caratterizzata dal valore del lato. Pertanto, classi derivate, ereditate dalla classe base CShape verranno dichiarate come segue:

//--- La classe derivata cerchio
class CCircle : public CShape          // Dopo il due_punti definiamo la classe base
  {                                    // dalla quale viene fatta l'eredità
private:
   int             m_radius;           // raggio del cerchio
 
public:
                   CCircle(){m_type=1;}// costruttore, type 1 
  };

La dichiarazione della classe della forma del Quadrato, è simile:

//--- la classe derivata Square
class CSquare : public CShape        // Dopo il due_punti definiamo la classe base
  {                                    // dalla quale viene fatta l'eredità
private:
   int            m_square_side;       // lato del quadrato
 
public:
                  CSquare(){m_type=2;} // costruttore, type 2 
  };

Occorre notare che mentre l'oggetto viene creato viene chiamato prima il costruttore della classe base, e poi viene chiamato il costruttore della classe derivata. Quando un oggetto viene distrutto viene prima chiamato il distruttore della classe derivata, e poi viene chiamato il distruttore della classe base.

Così, dichiarando i membri più generici nella classe base, possiamo aggiungere i membri addizionali nelle classi derivate, che specificano una classe particolare. L'ereditarietà consente di creare potenti librerie di codice che possono essere riutilizzate più volte.

La sintassi per creare una classe derivata da una già esistente è la seguente:

class class_name : 
          (public | protected | privateopt  base_class_name
  {                                    
   dichiarazione di membri della classe
  };

 

Uno degli aspetti della classe derivata è la visibilità (apertura) dei suoi membri successori (eredi). Le parole chiave public, protected e private vengono utilizzate per indicare il grado, con cui i membri della classe base saranno disponibili per qualla derivata. La parola chiave public dopo i due punti nell'intestazione di una classe derivata indica che i membri protetti e pubblici della classe base CShape devono essere ereditati come membri protetti e pubblici della classe derivata CCircle.

I membri della classe private della classe base non sono disponibili per la classe derivata. L'ereditarietà public anche significa che le classi derivate (CCircle e CSquare) sono CShapes. Cioè, il Quadrato (CSquare) è una forma (CShape), ma la forma non deve necessariamente essere un quadrato.

La classe derivata è una modifica della classe base, essa eredita i membri protetti e pubblici della classe base. I costruttori e distruttori della classe di base non possono essere ereditati. Oltre ai membri della classe base, nuovi membri vengono aggiunti nella classe derivata.

La classe derivata può includere l'implementazione di funzioni membro, diverse dalla classe base. Non ha niente in comune con l' overload, laddove il significato del nome della stessa funzione può essere diverso per le differenti sigle.

Nell' ereditarietà protected, i membri public e protected della classe base divengono membri protected della classe derivata. Nell' ereditarietà private, i membri public e protected della classe base divengono membri private ​​della classe derivata.

Nelle ereditarietà protected e private, la relazione che "l'oggetto di una classe derivata sia l' oggetto di una classe base" non è vero. Le ereditarietà protected e private sono rare, e ciascuna di esse deve essere usata con attenzione.

Si dovrebbe comprendere che il tipo di ereditarietà (public, protected o private) non influenza le vie di accesso ai membri delle classi base nella gerarchia di ereditarietà da una classe derivata. Con qualunque tipo di eredità, solo i membri della classe base dichiarata con gli specificatori di accesso public e protected saranno disponibili fuori dalle classi derivate. Prendiamolo in considerazione nel seguente esempio:

#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+--------------------------------------------------------------------------------+
//| Esempio di classe con un qualche tipo di accesso                               |
//+--------------------------------------------------------------------------------+
class CBaseClass
  {
private:             //--- Il membro private non è disponibile da classi derivate
   int               m_member;
protected:           //--- Il metodo protected è disponibile dalla classe base e dalle sue classi derivate
   int               Member(){return(m_member);}
public:              //--- Il costruttore della classe è disponibile per tutti i membri delle classi
                     CBaseClass(){m_member=5;return;};
private:             //--- Un metodo privato per l'assegnazione di un valore ad m_member
   void              Member(int value) { m_member=value;};
 
  };
//+--------------------------------------------------------------------------------+
//| Classe derivata con errori                                                     |
//+--------------------------------------------------------------------------------+
class CDerived: public CBaseClass // la specifica di ereditarietà pubblica può essere omessa, in quanto è predefinita
  {
public:
   void Func() // Nella classe derivata, definire una funzione con le chiamate ai membri della classe base
     {
      //--- Un tentativo di modifica di un membro private della classe base
      m_member=0;        // Errore, il membro private della classe base non è disponibile
      Member(0);         // Errore, il metodo private della classe base non è disponibile nelle classi derivate
      //--- Lettura del membro della classe base
      Print(m_member);   // Errore, il membro private della classe base non è disponibile
      Print(Member());   // Nessun errore, il metodo di protezione è disponibile dalla classe base e le sue classi derivate
     }
  };

Nell'esempio precedente, CBaseClass ha solo il metodo public - il costruttore. I costruttori vengono chiamati automaticamente quando si crea un oggetto di classe. Pertanto, il membro private m_member ed i medoti protected Member() non possono essere chiamati dall'esterno. Ma in caso di ereditarietà public, il metodo Member() della classe base sarà disponibile dalle classi derivate.

In caso di ereditarietà protected, tutti i membri della classe base con accesso public e protected diventano protected. Ciò significa che se i dati public dei membri e metodi della classe di base erano accessibili dall'esterno, con l'ereditarietà protected sono disponibili solo dalle classi della classe derivata e dei suoi ulteriori derivati.

//+--------------------------------------------------------------------------------+
//| Esempio di classe con un qualche tipo di accesso                               |
//+--------------------------------------------------------------------------------+
class CBaseMathClass
  {
private:             //--- Il membro private non è disponibile da classi derivate
   double            m_Pi;
public:              //--- Ottenere ed impostare un valore per m_Pi
   void              SetPI(double v){m_Pi=v;return;};
   double            GetPI(){return m_Pi;};
public:              // Il costruttore della classe è a disposizione di tutti i membri
                     CBaseMathClass() {SetPI(3.14);  PrintFormat("%s",__FUNCTION__);};
  };
//+--------------------------------------------------------------------------------+
//| Classe derivata, in cui m_Pi non può essere modificato                         |
//+--------------------------------------------------------------------------------+
class CProtectedChildClass: protected CBaseMathClass // Ereditarietà protected
  {
private:
   double            m_radius;
public:              //--- Metodi public nella classe derivata
   void              SetRadius(double r){m_radius=r; return;};
   double            GetCircleLength(){return GetPI()*m_radius;};
  };
//+--------------------------------------------------------------------------------+
//| Funzione di starting dello Script                                              |
//+--------------------------------------------------------------------------------+
voidOnStart()
  {
//--- Quando si crea una classe derivata, il costruttore della classe base verrà chiamato automaticamente
   CProtectedChildClass pt;
//--- Specificare il raggio
   pt.SetRadius(10);
   PrintFormat("Length=%G",pt.GetCircleLength());
//--- Se commentiamo la stringa qui di seguito, verrà visualizzato un errore in fase di compilazione, dal momento che SetPi () è ora protected
// pt.SetPI(3); 
 
//--- Ora dichiariamo una variabile della classe base e cerchiamo di impostare la costante Pi uguale a 10
   CBaseMathClass bc;
   bc.SetPI(10);
//--- Ecco il risultato
   PrintFormat("bc.GetPI()=%G",bc.GetPI());
  }

L'esempio mostra che i metodi SetPi() e GetPi() nella classe base CBaseMathClass sono aperti e disponibili per essere chiamati da qualsiasi punto del programma. Ma al tempo stesso, per CProtectedChildClass che è derivato da esso questi metodi possono essere chiamati solo dai metodi della classe CProtectedChildClass o sue classi derivate.

In caso di ereditarietà private, tutti i membri della classe di base con accesso public e protected diventato private, e chiamarli diventa impossibile in un' ulteriore eredità.

MQL5 non ha l'ereditarietà multipla.

Vedi anche

Strutture e Classi