Newsgroup: it.comp.lang.c++
Titolo: Perche' ti nascondi ?

Considerate le seguenti definnizioni
class base
{
...
    virtual void Dato(double d);
    virtual double Dato(void) const;
...
};
class derivata
{
...
    virtual void Dato(double d);
...
};
Se faccio una cosa del genere il C++Builder mi avverte in fase di
compilazione che la funzione virtual double Dato(void) const viene
'nascosta' : non mi rimane che ridefinirla come virtual double Dato(void)
const {return base::Dato();}.
Se non avessi definito come virtual la seconda funzione, non sarebbe stato
gerato un warning, ma comunque non sarebbe stata visibile !
Per quanto mi sforzi non riesco a darmi una ragione plausibile di un simile
comportamento. C'e' qualcuno che puo' spiegarmelo ? O suggerire un'altra via
piu' elegante ?
--
*** SPARTACO ***
-> 
amu...@tin.it
 <-
http://www.pegacity.it/informatica/case/473/index.htm

O suggerire un'altra via piu' elegante ?
Myers e altri mi hanno ormai convinto (e non solo me!-)
che usare lo stesso nome per un metodo che "legge"
un valore, e un altro che lo "scrive", e` una falsa eleganza
e non un uso ottimale del linguaggio (oltre a questo piccolo
problema emergono altre scocciature per i puntatori a
membri, l'uso ottimale di template, l'eredita` multipla e
virtuale, ecc -- senza NESSUN beneficio a compensare!)
La via piu` elegante e` invece la piu` semplice:
class base {
public:
    virtual void setDato(double d);
    virtual double getDato(void) const;
};
chiama sempre setQualcosa la routine che scrive, e
getQualcosa quella che legge, e non avrai mai problemi
di hiding per esse.  (Questa e` anche la forma in cui
il compilatore MIDL traduce i metodi di get e set di
una proprieta` da IDL a C++, eccetto che aggiunge
un underscore dopo set e get -- comunque la cosa
importante e` tenere i nomi distinti...).
I problemi restano quando l'overload e` _appropriato e
utile_, naturalmente, ma almeno togliamoci quelli dove
l'overload non serve proprio...!-)
Alex

Grazie dell'esauriente risposta, ma non mi hai propriamente convinto !
Proprio l'esempio che fai tu mi pare indicare una possibile situazione in
cui devrebbero esistere tutte e due le funzioni :
se tento di aggiornare una derivata con una base e' ovvio che non posso
aspettarmi altro che un aggiornamento parziale !
Ovviamente se decidiamo che non e' lecito potremmo sempre (se il c++ fosse
come lo sogno io) ridefinire la funzione in questione perchè sollevi
un'eccezione.
Se ho capito bene il tuo esempio, la differenza consiste nel fatto che
l'errore viene generato in compilazione (derived non puo' essere copiata con
una base).
Ovviamente in caso di funzionamento virtuale viene utilizzata la versione di
base e questo mi sembra doveroso, ma in contrasto con la logica che ha
portato a nascondere :
se scrivo
base *p,*q;
p = new derived();
q = new base();
p->Copy(q); // OK
se invece scrivo
base *q;
derived *p;
q = new base();
p = new derived();
p->Copy(q); // errore di compilazione !
Eppure viene (verrebbe) chiamata la stessa funzione nei due casi !
Comunque si tratta di una caso leggermente diverso : l'omonimia si ha tra
funzioni di classi diverse; Copy(derived *) non esiste in base.
Il fatto e' che puo' succedere che io abbia parecchie funzioni virtuali
omonime delle quali devo ridefinire una sola : sono costretto a ridefinirle
tutte !
Se invece non ridefinissi quell'unica, tutte le altre funzionerebbero
benissimo ! Non mi sembra molto coerente !
Per quanto riguarda i puntatori ai membri, ho un altro sassolino da
togliermi :
double stupida(double (*f)(double), double x1, double x2)
{
    return x2*f(x1);
}
class espr
{
    ...
    double val(double x);
    ...
};
mi piacerebbe moltissimo poter scrivere
{
    espr *x = new espr();
    ...
    stupida(x.val,1,1);
    ...
}
Non venirmi a dire che potrei aver fatto per qualche motivo delete x !
Se ho fatto una porcheria simile, peggio per me. A questa stregue
bisognerebbe immediatemenete introdurre nel c++ la garbage collection o
proibire un'infinita' di altre cose.
In questo caso specifico non sono riuscito a trovare qualcosa di decente per
utilizzare delle librerie c che richiedevano dei puntatori a funzione.
Mi potresti spiegare la ratio di questa scelta ?


Mi potresti spiegare la ratio di questa scelta ?
Spero di avere fatto di meglio, nel mostrarti come puoi tu
stesso costruirti il "boilerplate di infrastruttura" necessario
per realizzare tu stesso il kerning che ti serve.  Non ti
dovrebbe ora essere difficile generalizzare questa soluzione
per soddisfare tutte le tue esigenze in merito.
Ad esempio, se non ti basta avere un singolo puntatore a
funzione-bersaglio, ma ne vuoi potrere avere fino a tre,
arricchisci la classe holder di ulteriori "slot" di memoria
(ad esempio come vector<> per totale generalita`), poni
nel costruttore di adapter<> (e quindi in adapt<>) un altro
argomento di numero-progressivo, e usalo in getfun() per
selezionare una diversa funzione statica di adapter a
seconda dello slot cui deve accedere (f1, f2, f3).
Ti  sara` inoltre semplice rendere inline la gran parte delle
funzionalita` necessarie, ecc ecc.
Filosofia del C++: si puo` fare tutto quello che ha senso (e
parecchie cose che non ne hanno:-); fornire le funzionalita`
necessarie (sviluppando opportuni sistemi di template,
macro, gerarchie di classi, ecc ecc) e` spesso disagevole,
richiedendo discrete capacita` progettuali, buone competenze
informatiche, grande conoscenza del linguaggio e relative
librerie-standard, e non di rado tanto "boilerplate coding";
*usare* le funzionalita` fornite da un framework ben sviluppato
e` assai piu` agevole e normalmente abbastanza comodo.
Bibliografia consigliata: Lippmann, "C++ Object Model";
Coplien, "Advanced C++ Idioms"; Meyers, "Effective C++"
(seconda edizione) e "More Effective C++"; Gamma et al,
"Design Patterns"; e, naturalmente, tutta la vasta letteratura
informatica (ad esempio, non credo che nessuno di questi
testi menzioni il "kerning", ma qualsiasi buon testo avanzato
e aggiornato sulla scrittura di compilatori per linguaggi
funzionali potra` aiutare a sviscerare l'argomento).
Alex

Caro Alex, non capisco veramente perchè ti sei 'offeso'.
Avevo trovato molto interessante la tua risposta, e speravo di poter trarre
utili informazioni da qualcuno che mi sembrava molto preparato.
Forse ho sbagliato a non fare un reply solo personale ?
L'argomento è particolarmente 'inutile' e insignificante ?
Ho sestenuto delle fesserie ?
Non so.
Mi scuso comunque per qualsiasi cosa abbia potuto ledere la tua sensibilità.


Mi scuso comunque per qualsiasi cosa abbia potuto ledere la tua
sensibilità.
Mi scuso, a mia volta, se ho dato l'impressione che tu
abbia appunto leso questa sensibilita` (in realta` ho una
pelle da rinoceronte, senza la quale non si vive 10 anni
in rete...:-) -- dovevo evidentemente essermi spiegato
male -- spero che la questione sia ora chiarita, e che le
mie annotazioni e l'esempio di template per il kerning si
siano invece dimostrati utili.
Alex