Newsgroup: it.comp.lang.c++
Titolo: nomi dei metodi virtuali

Considerate il seguente esempio :
class A
{
    public :
    virtual int at(int i, int j) const {return i+j;}
};
class B : public A
{
    public :
    virtual int at(int i) const {return -i;}
};
mi aspetterei di poter scrivere :
A *p = new A(), *q = new B();
B *z = new B();
cout << p->at(1,1) << " " << q->at(1,1) << endl;
cout << z->at(1) << " " << z->at(1,1);
e di ricevere in out
2 -2
-1 -2
Invece ricevo un warning ("la funzione B::at(int) nasconde la funzione
virtuale A::at(int,int)") e un errore ("troppi  parametri nella chiamata di
B::at(int)").
Tutto si aggiusta se aggiungo la dichiarazione/definizione di at(int,int)
anche in B.
C'e' qualcuno che mi sa spiegara la ratio di un simile comportamento ?
Andrea Murru
amu...@usa.net
P.S. Uso il C++ Builder 3

C'e' qualcuno che mi sa spiegara la ratio di un simile comportamento ?
Una classe e` uno scope, e un identificatore definito in uno scope
interno (ad es in una classe derivata) *nasconde* [NON va in
overload con] uno definito in uno scope piu` esterno (ad es in
una classe base).  Stroustrup spiega e giustifica questa scelta
di progetto (come tante altre), in dettaglio, nel suo testo "Design
and Evolution of C++", Addison-Wesley.
Alex

and Evolution of C++", Addison-Wesley.
Sono molto curioso : non mi viene in mente alcun motivo valido (quanto sono
poco malizioso evidentemente ...) !
Perche' non ci dici qualcosa ?
Andrea Murru
amu...@usa.net

...

Non penso di poterlo spiegare cosi` efficacemente come
fa Stroustrup, comunque, uno dei criteri che egli ha usato
nel progetto del linguaggio C++ e` la regolarita`, cioe`
il fatto che una regola (nel caso pertinente: cosa succede
ad un nome definito in uno scope esterno che viene anche
ridefinito in uno logicamente annidato) sia applicata con
uniformita`, senza eccezioni ad hoc.  Ora, la regola
generale per questo caso e`, chiaramente, che il nome
definito nello scope annidato nasconde quello dello
scope esterno -- regola comune ai linguaggi che hanno
il concetto di scope (binding lessicale), e comunque non
alterabile in C++ per compatibilita` con C (in Java e`
definito come errore, non avendo vincoli di compatibilita`;
scelta insolita, ma interessante).
Consideriamo ad esempio queste classi:
class Inner1: private Outer {
public:
    static void pippo(int i) { std::cout << "pippo(" <<i<<")\n"; }
    static void pippo() { pippo('c'); }
};
class Inner2: private Outer {
public:
    static void pippo(int i='c') { std::cout << "pippo(" <<i<<")\n"; }
};
e il loro uso:
    Inner1 i1;
    Inner2 i2;
    i1.pippo();
    i2.pippo();
Con la regola attuale, so che i due comportamenti saranno
identici: in ambo i casi, otterro` la scritta "pippo(67)" (se il
codice dei caratteri e` ad es. un comune ISO 8859).  Se
aggiungo al programma una funzione globale
    void pippo(char c) { std::cout << "pippo(" <<c<<")\n"; }
questo NON cambia la semantica di Inner1 e Inner2: infatti,
ciascuna di esse e` uno "scope interno", la cui definizione del
nome pippo _nasconde_ quella dello scope piu` esterno (lo
scope globale).  Egualmente, se aggiungo una static void
Outer::pippo(char c), e per l'identica ragione (scope interno
nasconde scope piu` esterno).
Se gli scope fossero "trasparenti", la definizione "esterna"
di pippo (globale, o in Outer) "filtrerebbe" in Inner1 e Inner2,
con effetti diversi: Inner2 sarebbe robusta a questa modifica;
Inner1, invece, ne risulterebbe toccata, in quanto, in questa
ipotesi, i1.pippo() dovrebbe ora scrivere "pippo(c)" (per
miglior match dell'overload pippo(char) rispetto a quello
pippo(int), se lo scope venisse ignorato).
Stroustrup considera spiacevole imporre queste anomalie
e asimmetrie *per default* al programmatore, tanto piu`
quando, per ottenere questo, dovrebbe inoltre inserire una
specifica eccezione ad hoc alle regole, di per se abbastanza
semplici, relative allo scoping.  Tutto sommato, se l'autore
del codice *vuole* iniettare nello scope di Inner2 le varie
definizioni di un certo identificatore pippo nello scope piu`
esterno Outer, puo` agevolmente dire "using Outer::pippo",
esattamente come in ogni altro caso simile di rapporti fra
namespace -- nessuna irregolarita`, nessuna eccezione alle
normali regole di scoping, nessuna anomalia, e uno sforzo
supplettivo di programmazione decisamente modesto.
Naturalmente, si possono ipotizzare anche progetti di
linguaggi molto diversi, ad esempio basati su regole
diverse a seconda della natura degli identificatori in
gioco (funzioni piuttosto che dati, funzioni virtuali
piuttosto che non-virtuali e/o statiche, e cosi` via).
Molti linguaggi cercano di privilegiare la "comodita`"
del programmatore, introducendo appunto tante
eccezioni e regole ad hoc; purtroppo, l'effetto totale
di questo approccio, secondo me, tende a produrre
piu` confusione, piu` difficolta` nel determinare con
precisione la semantica di una data situazione di una
certa complessita`, rispetto alla maggior regolarita`
di un approccio con meno regole, piu` generali, e
senza eccezioni -- anche se quest'ultimo ti obbliga,
occasionalmente, a scrivere un "using Outer::pippo"
che l'approccio piu` ad hoc potrebbe evitarti.
Alex

Gratias per la risposta : ho qualcosa su cui riflettere.


Per curiosita` ho provato sul mio compilatore (BC++ 5.02) ed ho
notato che utilizzando la using il programma compila ed esegue
correttamente, ma la warning " 'B::at(int) const' hides virtual function
'A::at(int,int) const' " mi viene ancora segnalata.
Paolo
----------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
  e-mail: 
mar...@cli.di.unipi.it
 
----------------------------------------------------------

 senza eccezioni
Stai parlando del Visual Basic... :-(
EMG

 'A::at(int,int) const' " mi viene ancora segnalata.
Stessa cosa capita in C++Builder, e succede anche se implementi B::at(int, int).
E` un comportamento anomalo del compilatore Borland.
Ciao
Paolo

E` un comportamento anomalo del compilatore Borland.
Lo supponevo... comunque il builder usa lo stesso compilatore del BC++5.00
