Newsgroup: it.comp.lang.c++
Titolo: Ridefinizioni di funzioni in classi derivate

Mi trovo nella necessità di ridefinire
una funzione in una classe derivata,
la quale però viene impiegata anche
da una classe "intermedia" secondo
il seguente schema:
class Base {
  ...
  int func_alfa();
  ...
};
/****************************************/
class Intermediate : public Base {
  ...
  public :
  int func_beta () {
    ...
    localErr=func_alfa();
    ...
  }
  ...
};
/****************************************/
class LastClass : public Intermediate {
  ...
  int func_alfa () {
    ...
    /* RIDEFINIZIOINE DI  func_alfa() */
    ...
  }
};
/*****************************************/
void main() {
   LastClass  *MyLCPtr=new LastClass();
   ....
   /*Utilizzo la funzione func_beta()*/
   int mainErr=MyLCPtr->func_beta();
   ....
   if(MyLCPtr)    delete MyLCPtr;
   return;
}
Quello che mi chiedo a questo punto è :
visto che in func_beta() viene richiamata
func_alfa(), ed essendo ridefinita in LastClass,
quale delle due versioni di questa funzione
verrà eseguita ? E se all'interno del corpo
di func_beta la riga di codice
    localErr=func_alfa();
venisse sostituita con
    localErr=this->func_alfa();
oppure con
    localErr=Base::func_alfa();
?
Un ringraziamento anticipato a chiunque vorrà esprimere
il suo parere.
                                                        Lorenzo Girardin
                                                        Elettronica Palladio
s.r.l.









 di func_beta la riga di codice
Viene richiamata la funzione definita dal tipo dell'oggetto chiamante o
in caso di puntatori la funzione definita dal tipo dell'oggetto puntato.
Se non esiste si torna indietro nella gerarchia.
ciao.
-- 
     o===o        Filippo Salotti, Bagni di Lucca, Lucca, Italy.
    j$    %  _                  E-mail : 
fil...@humnet.unipi.it
   j$      O<_================================================o
 .oHHo.        $ Studente Ingegneria Informatica Automatica   $
OHXXXXHO       $ Dip. Sistemi Elettrici Universita' di Pisa.  $

    localErr=Base::func_alfa();
Questa, se func_alfa fosse virtuale, avrebbe
un significato drasticamente diverso: il costrutto
"nomeclasse::" anteposto a una funzione virtuale,
infatti, indica appunto di ignorare la natura
virtuale della funzione stessa.  Quindi verrebbe
eseguita la versione della classe-base anche se
func_alfa fosse virtuale (non essendo virtuale,
nessuna differenza).
Alex

Mi trovo nella necessità di ridefinire
una funzione in una classe derivata,
la quale però viene impiegata anche
da una classe "intermedia" secondo
il seguente schema:
class Base {
  ...
  int func_alfa();
  ...
};
/****************************************/
class Intermediate : public Base {
  ...
  public :
  int func_beta () {
    ...
    localErr=func_alfa();
    ...
  }
  ...
};
/****************************************/
class LastClass : public Intermediate {
  ...
  int func_alfa () {
    ...
    /* RIDEFINIZIOINE DI  func_alfa() */
    ...
  }
};
/*****************************************/
void main() {
   LastClass  *MyLCPtr=new LastClass();
   ....
   /*Utilizzo la funzione func_beta()*/
   int mainErr=MyLCPtr->func_beta();
   ....
   if(MyLCPtr)    delete MyLCPtr;
   return;
}
Quello che mi chiedo a questo punto è :
visto che in func_beta() viene richiamata
func_alfa(), ed essendo ridefinita in LastClass,
quale delle due versioni di questa funzione
verrà eseguita ? E se all'interno del corpo
di func_beta la riga di codice
    localErr=func_alfa();
venisse sostituita con
    localErr=this->func_alfa();


...
:     [snip]
: >class Base {
: >  int func_alfa();
: Da notare (fondamentale, e strano!): NON virtual.
Non e` necessariamente strano il fatto che la funzione sia non virtual.
L'uso della keyword virtual ha senso quando si usano oggetti polimorfi,
se una applicazione non necessita del polimorfismo (perche` il tipo esatto
degli oggetti e` sempre noto) si puo` evitarne l'uso.
: >class Intermediate : public Base {
: >  int func_beta () {
: >    localErr=func_alfa();
:     [snip]
: >class LastClass : public Intermediate {
: >  int func_alfa () {
: >    /* RIDEFINIZIOINE DI  func_alfa() */
:     [snip]
: >   LastClass  *MyLCPtr=new LastClass();
: >   ....
: >   int mainErr=MyLCPtr->func_beta();
:     [snip]
: >visto che in func_beta() viene richiamata
: >func_alfa(), ed essendo ridefinita in LastClass,
: >quale delle due versioni di questa funzione
: >verrà eseguita ?
: Poiche` func_alfa NON e` virtual, non puo`
: esserci dubbio: viene eseguita la funzione
: della classe base.
Non e` esatto, poiche` func_alfa e` non virtual essa viene linkata
a compile time seguendo le regole di scoping statico del C++.
Quindi in LastClass viene eseguita la versione locale di func_alfa.
Saluti,
Paolo

Quindi in LastClass viene eseguita la versione locale di func_alfa.
Hai, semplicemente, torto marcio.
Non ci credi?  Prova!  Ecco il codice completo:
#include <iostream>
using namespace std;
class Base {
public:
    void func_alfa() {
        cout<<"Base\n";
    }
};
class Intermediate: public Base {
public:
    void func_beta() {
        func_alfa();
    }
};
class Last_Class: public Intermediate {
public:
    void func_alfa() {
        cout<<"Last\n";
    }
};
int main()
{
    Last_Class x;
    x.func_beta();
    return 0;
}
Compilalo e fallo girare, e vedi un poco se trovi UN
solo compilatore C++ abbastanza bacato da farne un
eseguibile che emette "Last"... TUTTI i compilatori
C++ produrranno programmi che emettono "Base"!
E se dopo questo svarione che hai commesso,
"correggendo" le mie corrette affermazioni con la
tua bella perla, pensi ancora che ridefinire in una
classe derivata una funzione NON virtual di una
classe base non sia *veramente* _strano_... beh,
meglio astenersi da ulteriori commenti!
Alex

Alex Martelli (
al...@magenta.com
) wrote:
: >Non e` necessariamente strano il fatto che la funzione sia non virtual.
: E` MOLTO strano che una funzione non virtual venga
: "nascosta" lessicalmente (perche` questo e` quel che
: succede, NON un "override"!) definendone un'altra
: dello stesso nome in una classe derivata.  L'unico caso
: ragionevole e` per classi (e funzioni) destinate ad essere
: usate solo entro template.
: >L'uso della keyword virtual ha senso quando si usano oggetti polimorfi,
: >se una applicazione non necessita del polimorfismo (perche` il tipo esatto
: >degli oggetti e` sempre noto) si puo` evitarne l'uso.
: Ma non si deve in questo caso "intorbidare le acque" con
: un nascondimento lessicale di questo genere, che non ha
: nessuna funzione utile (esclusi certi casi di template) e puo`
: solo confondere le idee.
Quanto dici conferma sostanzialmente cio` che io ho affermato, ovvero
che ci sono casi in cui la mancanza della keyword virtual non e` un fatto strano.
Anzi ci sono casi in cui l'uso di virtual e` potenzialmente pericoloso,
in particolare nel caso di funzioni membro che vengono richiamati dalla
classe stessa.; in questi casi si favorisce infatti la nascita
del cosi` detto "fragile base class problem" stranoto in letteratura per
essere uno dei maggiori ostacoli alla diffusione della OOP (non per nulla
MS ha introdotto COM).
Se ne vuoi sapere di piu` cerca su 
www.programmers.net/Pescio
.
In merito al quesito proposto riconosco comunque il mio errore. Non mi
sono accorto della mancata ridefinizione della funzione LastClass::func_beta()
Ed e` piu` strano tale omissione della mancanza della keyword virtual (se
partiamo dal presupposto di fare a meno del late binding), in ogni caso
situazioni del genere visto nell'esempio, mi portano a pensare a possibili
problemi nel design della gerarchia di classi.
: E se dopo questo svarione che hai commesso,
: "correggendo" le mie corrette affermazioni con la
: tua bella perla, pensi ancora che ridefinire in una
: classe derivata una funzione NON virtual di una
: classe base non sia *veramente* _strano_... beh,
: meglio astenersi da ulteriori commenti!
A parte il fatto che hai detto che era strana la mancanza di virtual
e non il fatto che si stesse ridefinendo una funzione non virtual, non ho
detto che la cosa non e` strana, ma che non e` necessariamente strana...
ma eri troppo preso dal rimproverarmi l'errore, da non aver capito neanche dove
ho sbagliato.
Paolo

ho sbagliato.
DOVE hai sbagliato (un caso fra i tanti) e` asserendo recisamente
e con sicumera il falso, PERCHE` hai sbagliato e` un'altra questione,
forse piu` interessante per esperti di psicologia anormale che di C++,
comunque non c'e` dubbio che il *contesto* del tuo svarione e` l'uso
di "ridefinizioni di funzioni in classi derivate" (vedi subject) _per
funzioni non virtuali_ (ovvero, che e` lo stesso, la non virtualita` di
funzioni ridefinite in classi derivate), uso deprecabile e appunto per
questo strano e da prendere con le molle.
Alex