Cerca nel sito:
ricerca
avanzata

Frasi Celebri...

L'arte ? una collaborazione tra l'uomo e Dio, e meno l'uomo fa, meglio

Andr? Gide 

Sondaggio:

Quale sistema operativo preferite?

Linux
Windows
DOS
MacOs
UNIX

visualizza risultati


 

Accesso ai campi ereditati

 

La classe derivata puo` accedere ai membri protetti e pubblici della classe base come se fossero suoi (e in effetti lo sono):


  class Person {
    public:
      Person();
      ~Person();
      void PrintData();
      void Sleep();

    private:
      char* Name;
      unsigned int Age;
      /* ... */
  };

/* Definizione dei metodi di Person */


  class Student : Person {
    public:
      Student();
      ~Student();
      void DoNothing();   // Metodo proprio di Student

    private:
      unsigned int IdCode;
      /* ... */
  };

  void Student::DoNothing() {
    Sleep();              // richiama Person::Sleep()
  }


Il codice ereditato continua a comportarsi nella classe derivata esattamente come si comportava nella classe base: se Person::PrintData() visualizzava i membri Name e Age della classe Person, il metodo PrintData() ereditato da Student continuera` a fare esattamente la stessa cosa, solo che riferira` agli attributi propri dell'istanza di Student su cui il metodo verra` invocato.

In molti casi e` desiderabile che una certa funzione membro, ereditata dalla classe base, si comporti diversamente nella classe derivata. Come alterare dunque il comportamento (codice) ereditato? Tutto quello che bisogna fare e` ridefinire il metodo ereditato; c'e` pero` un problema, non possiamo accedere direttamente ai dati privati della classe base. Come fare?
Semplice riutilizzando il metodo che vogliamo ridefinire:


  class Student : Person {
    public:
      Student();
      ~Student();
      void DoNothing();
      void PrintData();    // ridefinisco il metodo

    private:
      unsigned int IdCode;
      /* ... */
  };

  void Student::PrintData() {
    Person::PrintData();
    cout << "Matricola: " << IdCode;
  }


Poiche` cio` che desideriamo e` che PrintData() richiamato su una istanza di Student visualizzi (oltre ai valori dei campi ereditati) anche il numero di matricola, si ridefinisce il metodo in modo da richiamare la versione ereditata (che visualizza i campi ereditati) e quindi si aggiunge il comportamento (codice) da noi desiderato.
Si osservi la notazione usata per richiamare il metodo PrintData() della classe Person, se avessimo utilizzato la notazione usuale scrivendo


  void Student::PrintData() {
    PrintData();
    cout << "Matricola: " << IdCode;
  }


avremmo commesso un errore, poiche` il risultato sarebbe stato una chiamata ricorsiva. Utilizzando il risolutore di scope (::) e il nome della classe base abbiamo invece forzato la chiamata del metodo PrintData() di Person.

Il linguaggio non pone alcuna limitazione circa il modo in cui PrintData() (o una qualunque funzione membro ereditata) possa essere ridefinita, in particolare avremmo potuto eliminare la chiamata a Person::PrintData(), ma avremmo dovuto trovare un altro modo per accedere ai campi privati di Person. Al di la` della fattibilita` della cosa, non sarebbe comunque buona norma agire in tal modo, non e` bene ridefinire un metodo con una semantica differente. Se Person::PrintData() aveva il compito di visualizzare lo stato dell'oggetto, anche Student::PrintData() deve avere lo stesso compito. Stando cosi` le cose, richiamare il metodo della classe base significa ridurre la possibilita` di commettere un errore e risparmiare tempo e fatica.
E` per questo motivo infatti che non tutti i membri vengono effettivamente ereditati: costruttori, distruttore, operatore di assegnamento e operatori di conversione di tipo non vengono ereditati perche` la loro semantica e` troppo legata alla effettiva struttura di una classe (il compilatore comunque continua a fornire per la classe derivata un costruttore di default, uno di copia e un operatore di assegnamento, esattamente come per una qualsiasi altra classe e con una semantica prestabilita); il codice di questi membri e` comunque disponibile all'interno della classe derivata (nel senso che possiamo richiamarli tramite il risolutore di scope ::).

Naturalmente la classe derivata puo` anche definire nuovi metodi, compresa la possibilita` di eseguire l'overloading di una funzione ereditata (naturalmente la versione overloaded deve differire dalle precedenti per tipo e/o numero di parametri). Infine non e` possibile ridefinire gli attributi (membri dato) della classe base.

 

successivo
–«  INDICE  »–

 

 

 

 
Powered by paper&pencil (carta&matita ) - Copyright © 2001-2022 Cataldo Sasso