Newsgroup: it.comp.lang.c++
Titolo: ereditarietà

Ciao a tutto il NG!
C'è qualche buona anima dotata di enorme pazienza che saprebbe spiegare ad
un testone come me l'ereditarietà?
Quel che non capisco di questa ca... di ereditarietà è questo?
1)I dati che vengono ereditati alla fine diventano parte della classe
derivata oppure no?
2)Che bisogno c'è di chiamare il costruttore della classe base per
inizializzare i dati ereditati se questi vengono a fare parte della classe
derivata?
3)Come può un puntatore della classe base puntare ad un oggetto della classe
derivata?(da quel che ne so un puntatore può pontare solo ad una variabile o
un oggetto del suo stesso tipo, come può puntare ad un oggetto che è solo
parzialmente del suo stesso tipo?)
Grazie mille a chiunque mi risponderà!
Ciao
Francesco!

Francesco Rossi wrote:

Ciao,
bella domanda. Ci sarebbe molto da dire, ma mi limito a rispondere a quello 
che tu chiedi, poiché la cosa è molto vasta e ti conviene cercare su 
qualche testo relativo al C++, piuttosto che chiedere su un NG :-)))
Se B è una classe e D è un'altra classe che eredita da B, la classe B viene 
detta classe base, mantre D classe derivata...
la classe derivata, eredita sempre tutti i campi della classe base, e quindi 
di fatto ha le funzioni e proprietà definiti nella classe base.
Ma attenzione i campi privati della classe B non sono accessibili dalla 
classe D, poiché sono per l'appunto privati... se vuoi rendere accessibili 
dei campi alle classi derivati, ma non all'esterno della classe, devi 
dichiararli nella classe base come protetti (usando protected e non 
private); es:
class B {
public:
B();
B(int , int , int);
void foo();
void bar();
protected:
void foo2();
void bar2();
private:
void foo3();
void bar3();
int a, b, c;
};
class D: public B {
public:
void Display();
private:
int num;
int schermo;
};
la classe D ha dei campi suoi propri che sono Display, num e schermo 
dichiarati contestualmente alla sua definizione... ma come classe derivata 
da B ha in aggiunta ai campi suoi propri, TUTTI i campi di B; di questi 
potrà accedere dalle sue funzioni proprie esclusivamente i campi protetti e 
pubblici, mentre i campi privati saranno asi presenti, ma accessebili solo 
ed esclusivamente dalle funzioni di D ereditatate da B.
E' chiaro?
Io ho usato l'ereditarietà pubblica (usando public) che non altera la 
visibilità dei campi ereditati dalle classe base! esistono altri due tipi 
di ereditarietà, quella privata e quella protetta, che invece consentono di 
alterare la visibilità dei campi ereditati nella classe derivata. Ti faccio 
una tabellina:
CLASSE BASE     |       CLASSE DERIVATA (con public)
campi pubblici       |     pubblici
 "    protetti             |     protetti
"     privati              |     inaccessibili
CLASSE BASE     |       CLASSE DERIVATA (con protected)
campi pubblici       |     protetti
 "    protetti             |     protetti
"     privati              |     inaccessibili
CLASSE BASE     |       CLASSE DERIVATA (con private)
campi pubblici       |     privati
 "    protetti             |     privati
"     privati              |     inaccessibili
spero sia chiaro!
Quando la derivazione è pubblica, un oggetto della classe derivata è anche 
un oggetto della classe base, ovvero esiste una conversione di tipo che 
consente di utilizzare un oggetto della classe derivata in tutti i contesti 
in cui è ammesso l'utilizzo di un oggetto della classe base. Un altro modo 
per dirlo è: il tipo della classe derivata è un SOTTOTIPO del tipo della 
classe base; in questo senso la derivazione pubblica consente di 
organizzare i tipi di dato definiti da utente in una gerarchia concettuale.
Quanto detto non è vero se la derivazione nnon è pubblica, ma protetta o 
privata perché cambiano le regole di accesso ai campi!
Veniamo al costruttore; se per una classe non viene definito esplicitamente 
alcun costruttore, il compilatore rende automaticamente disponibile un 
costruttore standard senza argomenti. Per la verità questo costruttore c'è 
sempre, anche se lo definisci esplicitamente (quello senza argomenti!!!), 
ma il compilatore ne inibisce il funzionamento quando viene definito un 
qualunque costruttore!!
Se invochi un costruttore della classe derivata, automaticamente verrà 
eseguito anche il costruttore senza argomenti della classe base, definito o 
fornmito dal linguaggio. Se la classe derivata non ha costruttori, essa ha 
quello standard, fornito dal linguaggio, che invocherà ancora il 
costruttore senza argomenti della classe base.
Quest'invocazione automatica serve ad inizializzare i campi che la classe 
derivata ha ereditato da quella base. Se voglio fare in modo che 
l'invocazione di un costruttore della classe derivata inizializzi i campi 
ereditati ad un valore dato al momento della dfinizione dell'oggetto, posso 
fare così:
D(int disp, int n, int _a, int _b, int _c): B(_a, _b, _c) {
//fai quello che ti pare con i campi propri della classe derivata
//quelli ereditati sono inizializzati dall'invocazione 
//esplicita del costruttore a tre argomenti 
//della classe base
}
Supponiamo di avere:
void F(B b) {
}
void G(B& b) {
}
B base;
D derivato;
Le seguenti invocazioni sono tutte corrette:
F(base);
F(derivato);
G(base);
G(derivato);
Perche derivato è un'oggetto anche della classe B, essendo la derivazione 
pubblica (non sarebbe vero per derivazioni protette e private).
Per i puntatori si ha la stessa cosa.
In realtà ci sarebbe una differenza tra F e G (o il caso dei puntatori). Ma 
ti consiglio un buon testo sull'argomento :-)))
S.

 ha scritto nel messaggio
news:NLOJ8.987$QT3....@twister2.libero.it...


 Francesco
--
ciao,
Cecco
---------
Visit: 
www.sourceforge.net/projects/dlab
Home Page: 
http://dlab.sourceforge.net/
Dlab-developer mailing list
https://lists.sourceforge.net/lists/listinfo/dlab-developer

 oggetto della classe base:
[CUT]
Vorrei fare una precisazione. Non è affatto vero che il costruttore della
classe derivata può inizializzare solo i suoi. In realtà potrebbe a meno che
non siano privati della classe base, cosa peraltro consigliabile. Inoltre la
tua espressione "L'ereditarietà equivale, a tutti gli effetti, a dichiarare


 non siano privati della classe base, cosa peraltro consigliabile. 
Precisazione per precisazione una classe derivata *non* puo'
inizializzare i membri dati di una sua superclasse, perche' quando
inizia ad eseguire il suo costruttore tali membri dati sono gia'
inizializzati.
La classe derivata puo' pero' modificare i membri pubblici e protetti di
una sua superclasse, anche dal corpo del proprio costruttore (non dalla
lista di inizializzazione).
Ciao,
Nicola

 Francesco!
Ci provo. L'ereditarietà (pubblica) è il meccanismo attraverso cui si
realizzano le relazioni di generalizzazione-specializzazione. Questa
relazione, una delle più importanti del paradigma OO, nonchè una delle più
difficili da usare come si deve, modella una relazione del tipo "Un cane è
un animale" oppure "Uno studente è una persona", ovvero <specializzazione>
Is-A (E' un) <generalizzazione>. Praticamente la cosa si ottiene facendo in
modo che i nuovi oggetti abbiano i membri e le funzione propri più quelli
della classe base con la stessa visibilità ( public in public, protected in
protected e private in private ). Faccio un esempio.
class Persona
  {
  public:
    Persona( const std::string& PersNome ) : nome( PersNome ), /* altri dati
*/ {}
    virtual ~Persona() {}
  private:
    std::string nome;
    /* altri dati */
  };
class Studente : public Persona
  {
  public:
    Studente( const std::string& StudName, const std::string& StudCorso ) :
Persona(StudName), corso(StudCorso) {}
    virtual ~Studente() {}
  private:
    std::string corso;
    /* altri dati */
  };
nell'esempio dato abbiamo una classe Persona che definisce le
caratteristiche della famiglia (nel senso biologico) delle persone. La
classe studente è una specializzazione di una persona (ciò significa che è
si una persona, ma ha qualche caratteristica in più, il che la rende parte
di un dominio più specializzato di quello di Persona). Ma veniamo alla prima
richiesta che avevi fatto: perchè per inizializzare i dati di Persona
bisogna richiamare il suo costruttore e non si può farlo direttamente in
Studente? Il fatto tecnico è che i dati di persona sono privati, ciò
significa che non sono accessibili da Studente neanche utilizzando
ereditarietà pubblica (che fa ereditare dati e funzioni pubblici e protetti
con la stessa visibilità) e quindi bisogna necessariamente chiamare il
costruttore di Persona. C'è comunque una ragione di stampo teorico. Ogni
classe definisce, implicitamente od esplicitamente a seconda del linguaggio,
dei vincoli chiamati precondizioni, postcondizioni ed invarianti. Le
precondizioni e le postcondizioni sono specifiche di ogni metodo e sono i
requisiti per poter chiamare la funzione e le garanzie che la funzione dà,
rispettivamente. Le invarianti sono le condizioni che determinano lo stato
di consistenza di un oggetto (pensa ad un quadrato con uno dei lati di
misura zero). Ora, i costruttori esistono proprio perchè al momento della
costruzione un oggetto si trovi in uno stato consistente in modo da essere
usato, e quindi inizializzano i dati in modo da rispettare le invarianti. Se
all'interno di una classe derivata non si richiama il costruttore della
classe base, si potrebbero inizializzare i dati di persona in maniera
scorretta, correndo il rischio di ritrovarsi con valori sballati.
Certo il discorso non si esaurisce qui. Si dovrebbe parlare del principio di
sostituibilità di Liskov, della temporaneità di alcune specializzazione,
dell'uso (scorretto) che si fa dell'ereditarietà per modellare il
contenimento invece delle relazioni gen-spec, etc.
Ma una mail non basta!
--
*---------------------------------------
* Fulvio Esposito
* E-Mail: 
ful...@libero.it
* WWW: 
http://digilander.iol.it/fesposito
*---------------------------------------

 inizializzati.
Grazie mille!
Ciao
Francesco!
"Nicola Musatti" <Nicola....@ObjectWay.it> ha scritto nel messaggio
news:3D085B4B...@ObjectWay.it...


Qualcuno mi potrebbe spiegare, sempre in questo ambito la questione che un
ogetto quando si entra all'interno del costruttore è già costruito?
Se io metto la chiamata al costruttore della classe base all'interno del
corpo del costruttore della classe derivata, invece che fuori, il
costruttore di default della classe base viene chiamato oppure no?
Grazie mille!
Francesco

 chiamato oppure no?
In ogni gruppo di costruttori (allo stesso livello di derivazione)
soltanto uno viene eseguito.
Nella initialization-list puoi invocare un costruttore della classe base,
nel qual caso il costruttore di default non verrà eseguito.
Nella initialization-list non puoi invocare un altro costruttore della
classe derivata.
Max