Newsgroup: it.comp.lang.c++
Titolo: Problema con classi derivate

Problema con classi derivate:
Classe base     -->        Classe_A
                            /    \
                           /      \
                          /        \
Classi derivate --> Classe_B      Classe_C
                          \        /
                           \      /
                            \    /
Classe derivata -->        Classe_D
class Classe_A
{
public:
	virtual int leggi_pippo() {return NULL;}
	.....
};
class Classe_B : virtual public Classe_A
{
public:
	int leggi_pippo() {return pippo;}
	....
}
class Classe_C : virtual public Classe_A
{
	......
}
class Classe_D : virtual public Classe_B, virtual public Classe_C
{
	......
}
Le classi sono state dichiarate come virtuali per avere
(nella Classe_D), una sola Classe_A istanziata.
warning C4250: 'Classe_D' : inherits 'Classe_B::leggi_pippo'
via dominance
Questo e' un warning che mi viene fuori in compilazione.
E' naturale che faccia riferimento alla classe derivata Classe_B,
e allora perche' il warning?
Come faccio a eliminarlo ?
Probabilmente devo ridichiarare (dentro Classe_D) o fare esplicitamente
riferimento alla dichiarazione di leggi_pippo dentro Classe_B ?
Ogni commento e suggerimento e' ben accetto.
Grazie anticipate!
Ciao Fabrizio

 Questo e' un warning che mi viene fuori in compilazione.
Ho provato a compilare l'esempio con il DJGPP ultima versione e non mi
ha dato alcun warning anche se erano tutti abilitati.
EMG

Mi sono dimenticato di dire che uso il Visual C++ 6.0
 (anche la 5.0 dava lo stesso warning )

Come faccio a eliminarlo ?
#pragma warning(disable: 4250)
Normalmente non mi piace disabilitare i warning, ma "quanno
ce vo', ce vo'".
Alex

riferimento alla dichiarazione di leggi_pippo dentro Classe_B ?
Anche queste sono soluzioni che dovrebbero funzionare.
Paolo
----------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
  e-mail: 
mar...@cli.di.unipi.it
 
----------------------------------------------------------

che ti interessa esportare.
Intendi con "using Classe_C::leggi_pippo;"?  Questa, purtroppo, non
funziona con la necessaria flessibilita` se leggi_pippo ha degli overload
(si applica a _tutti_ gli overload...).  Comunque, tale using declaration
tocca l'accessibilita` e la visibilita`, ma non la dominanza.
Per cambiare la dominanza sarebbe necessario (e agghiacciante) il
codice *totalmente ridondante* consistente in una *ulteriore* (inutile)
override di tutti i virtuali gia` risolti per dominanza, all'unico scopo di
"delegare" in ciascun caso alla STESSA funzione che gia` sarebbe stata
usata, senza tutto questo minuetto; il tutto all'unico scopo di aggirare
un warning spurio del compilatore...!
Alex

 utenti del compilatore stesso, e quindi, nel dubbio, avvertono.
Non che io la capisca meglio, anzi, però sono d'accordo con te. :-)
Comunque, come ho già detto, forse quelli del DJGPP (GCC) la capiscono
meglio in quanto non danno alcun warning.
Secca se continuo a riferire cose specifiche del DJGPP/GCC? Siccome è
uno dei compilatori free più famosi, diffusi e multipiattaforma penso
che parlarne possa essere di interesse generale. Sbaglio?
EMG

 che parlarne possa essere di interesse generale. Sbaglio?
A me non secca assolutamente, anzi.
-- 
Roberto Gerola
Email: 
roge...@tin.it
WWW: 
http://space.tin.it/computer/rgerola


E` chiaro che un warning, una volta determinata l'inconsistenza del pericolo,
puo` semplicemente essere disattivato...


 dovrebbe fare se vi fosse ambiguita`).
Perchè dovrebbe esservi una sola leggi_pippo(), visto che Classe_B la
ridefinisce?
Chi o cosa stabilisce se dobbiamo usare la leggi_pippo() di B oppure quella di A
(ereditata attraverso a C)?
Sicuramente comunque non eredita due volte la stessa cosa, visto che se non
ereditasse anche da B, avrebbe la leggi_pippo() di A - ben diversa da quella di
B.
Grazie al fatto che le derivazioni di B e C da A sono virtuali, il compilatore
decide (IMO arbitrariamente anche se logicamente) di utilizzare la leggi_pippo()
ridefinita in B - ma questo non mi pare ne' ovvio ne' scontato.
Trovo quindi giusto che il C++ ti comuniche questo fatto, al quale potresti non
aver pensato.
E' comunque possibile che mi sia sfuggito qualcosa nella questione, non essendo
un gran amante della eredità multipla, nel qual caso vi prego di farmelo
presente. :-)
saluti,
	Denis [new entry in questo ng!]

presente. :-)
La regola della dominanza e` identica indipendentemente dalla moltiplicita`
o singolarita` dell'eredita` in gioco.
Il punto e` che simili warning sono in totale conflitto con il sogno (che alcuni
anche di recente in questo gruppo e it.comp.lang.c hanno presentato come
se fosse una realta`!) di compilare sempre senza warning, cioe` correggendo
eventuali imperfezioni segnalate dal compilatore.  Quella che il compilatore
(VC++) qui segnala con un warning non e` una imperfezione, ma un principio
fondamentale del linguaggio stesso: per compilare senza warning, si dovrebbe
forse dunque rinunciare a usare il linguaggio?!  (O "spegnere i warning", al
che l'obbiettivo di "compilare senza warning" diventa una burletta...:-).
Alex

 che parlarne possa essere di interesse generale. Sbaglio?
Penso che non sbagli, soprattutto se il riferimento fosse a egcs (la
versione attuale e corrente di gcc), che tra l'altro nell'attuale versione
1.1.1 e` finalmente decentemente vicino allo standard (come linguaggio
in senso stretto; purtroppo la libreria libstdc++ e` molto indietro, ma
gia` molto meglio nello snapshot pre-beta, si puo` sperare che si
adegui abbastanza presto).
Alex

puo` semplicemente essere disattivato...
O alternativamente, "accettato", che e` piu` o meno lo stesso (piu`
comodo, magari, disabilitarlo).  In un caso o nell'altro non si sta pero`
in realta` "compilando senza warning", come in un altro thread (anche
su it.comp.lang.c) c'e` chi sostiene di fare sempre.
Alex

 che l'obbiettivo di "compilare senza warning" diventa una burletta...:-).
Per me è una cosa molto seria. Comunque, se proprio non accetti (a
ragione...) un'affermazione assolutistica, diciamo allora che sarebbe
molto ma molto meglio che un programma venisse compilato senza warnings.
E se proprio devono essercene allora che siano il meno possibile
altrimenti rischiano di nasconderne qualcuno veramente importante. Dico
eresie?
EMG

 su it.comp.lang.c) c'e` chi sostiene di fare sempre.
E va bene, piantala di infierire... :-))
EMG

sapr=E0 solo in esecuzione di quale tipo =E8 l'oggetto che la invoca?
Ci sono casi in cui e` possibile determinare a compile time quale funzione
chiamare, in questi casi il compilatore potrebbe benissimo generare codice inline
(ad esempio se stai utilizzando un puntatore alla classe maggiormente derivata,
allora la funzione virtuale che invochi e` esattamente quella della classe maggiormente
derivata; oppure potresti aver utilizzato il qualificatore :: per risolvere a compile time
il binding e quindi ancora una folta il compilatore sapendo quale funzione invocare
puo` generare il corrispettivo codice inline...).
Naturalmente negli altri casi non sarebbe possibile risolvere staticamente la chiamata e
quindi niente espansione inline.


Purtroppo e` deprecata dallo Standard (a favore della using),
C'e` un motivo ben preciso per cui e` stata deprecata?
Apparentemente non mi sembra una cosa pericolosa.


 saprà solo in esecuzione di quale tipo è l'oggetto che la invoca?
Una funzione virtuale inline puo` essere espansa inline solo quando
e` invocata su di un oggetto il cui tipo-dinamico e` noto compile-time
(quindi, quando si usa il fatto che e` inline, non si usa il fatto che e`
anche virtuale!).  Puo` dunque servire se spesso viene chiamata
direttamente su _oggetti_ (non puntatori o reference) e "ogni tanto"
invece in modi virtualizzabili (su reference o puntatori).  Combinazione
non frequente (anche se tutt'altro che sconosciuta).  E se non si e`
certi della qualita` del proprio ottimizzatore si rischia un certo overhead
(ad esempio un compilatore sempliciotto potrebbe generare copie
non strettamente necessarie del corpo della funzione virtuale inline).
Alex

 eresie?
No, dici sacrosante verita`, quando esprimi dei *desiderata* -- purche`
si precisi che il "senza warning" NON va raggiunto rendendo il compilatore
"di bocca buona", bensi` scrivendo il proprio codice con precisione.
A proposito, col tuo egcs, gcc, ecc, ti ricordi di usare sempre
    -Wall -pedantic
???
Se no, il suo livello di warning di default e` ridicolmente basso
(equivalente quasi a "disabilita tutti i warning" della maggior parte
dei compilatori commerciali); il livello di warning di default dei
compilatori commerciali tende a essere piu` "severo".
Alex

Apparentemente non mi sembra una cosa pericolosa.
Nella forma da te usata (con le parentesi dopo il nome) non e`
mai stata ampiamente supportata (in effetti non conosco un
compilatore che la supporti).  La forma che era un "mezzo
standard di fatto" in passato era invece:
    class A: private B {
    public:
        B::pippo;    // NB: senza qualificazione
    };
"sostanzialmente" equivalente alla forma prevista invece
dallo standard, in cui il corpo della classe diventa
        using B::pippo;
Il problema e` che la semantica della forma all'antica,
senza "using", non era mai stata particolarmente limpida.
La maggior parte dei compilatori la usava identicamente
a come oggi si usa la using-declaration: un identificatore
di un altro namespace (usando una classe come una
specie di namespace...) viene "importato" nel namespace
corrente (idem idem), che pero` puo` fare ulteriori overload
_o specifiche ridefinizioni di alcuni specifici overload_ di
quell'identificatore.  Alcuni compilatori, invece, prendevano
questa "importazione" in modo piu` restrittivo.  Ad esempio:
class B {
protected:
    void pip(int i) { std::cout << "In B: " << i << "\n"; }
};
class A: private B {
public:
    using B::pip;    // oppure solo: B::pip;
    void pip(float i) { std::cout << "In A: " << i << "\n"; }
};
int main()
{
    A a;
    a.pip(1.23);
    return 0;
}
E` corretto questo programma?  Secondo lo standard
(e l'uso della maggior parte dei compilatori antichi) NO:
la chiamata a.pip(1.23) e` _ambigua_.  Ma alcuni
compilatori antichi consideravano invece l'overload
di pip specificamente definito in A un "miglior match"
di quello "importato" con la "B::pip" -- tra l'altro, che io
sappia, non esistono specifiche precise di questo senso
(ampliato e scorretto) di "miglior match".
Usando solo e rigorosamente
    using B::pip;
invece del "piu` o meno quasi eguale, forse"
    B::pip;
si hanno dunque due vantaggi:
    -- non si rischia di incappare in un compilatore
        antico che per compatibilita` interpreta la
        seconda forma diversamente dalla prima,
    -- non si hanno due modi equivalenti di dire la
        stessa identica cosa
Purtroppo, lo Standard non prevede (come lo standard
di fatto di un tempo non prevedeva) modi di "importare"
(fra classi, o, ora, fra namespace) solo specifici
overload di un dato identificatore; o di importare
diversi overload con diversa accessibilita`; ecc ecc.
Non e` un grosso problema nella maggior parte dei
casi (per funzioni non virtuali, ad esempio, l'uso di
esplicite funzioni inline di forwarding e` "palloso", come
sempre lo e` il "boilerplate", ma pazienza), ma per lo
specifico problema di funzioni virtuali in overload e`
notevole il rischio che il compilatore non sia in grado
di ottimizzare le risultanti "virtual inline" (rischi notevoli
di "code bloat", e comunque di overhead a runtime).
Per aggirare questi problemi, e` spesso meglio usare
l'incapsulamento (cosi` che le funzioni forwarding
inline siano non-virtuali) invece dell'eredita` privata
(che porterebbe alla virtualita` delle funzioni).
Alex

 Il punto e` che simili warning sono in totale conflitto con il sogno... 
[snip]
Se tale caso è esplicitamente chiaro dallo standard, non posso che darti (per
quel che può contare) la mia solidarietà...
D'altro canto molti si avvicinano al C++ in maniera "artigianale", forti del
fatto di sentire il C alla stregua dell'italiano, e quindi può essere sensato
che
il compilatore sia più prolisso nei punti inerenti la OOP.
Inoltre, se chiami l'help sul contestato warning, troverai scritto che si tratta
solo di un
chiarimento riportato per scrupolo.
saluti,
	Denis

solo di un chiarimento riportato per scrupolo.
E quindi e` sicuramente inappropriato che si trovi al livello di
warning di default (/W3, mi pare); dovrebbe, se mai, essere al
livello piu` alto, pignolo, e "certosino" (/W4, se non sbaglio),
quello che nessuno usa mai perche` a quel livello anche gli header
di sistema vengono bocciati da valanghe di warning...:-).
Alex

 using std::cout;
Chiarissimo tutto il discorso e anche quello sulle virtual inline.
Grazie. A proposito: si può scrivere virtual inline, inline virtual o
entrambe le forme? Il DJGPP le accetta entrambe...
EMG

 ???
Ehm, normalmente si, anche se nell'ultimo aggiornamento mi era sfuggito
il -pedantic. Che tu sappia ci sono altri switch che permettono di
aumentare ancora il livello degli warnings?
EMG

 di sistema vengono bocciati da valanghe di warning...:-).
Non ho verificato a che livello sia - ma io uso quasi sempre quello "certosino",
visto che non sono mai troppo sicuro di quello che faccio ;-)
saluti,
	Denis

 entrambe le forme? Il DJGPP le accetta entrambe...
Prego.  Entrambe le forme sono corrette.  Inoltre, inline e`
implicito se una funzione e` definita entro la dichiarazione
della classe (virtual o meno che sia).
Alex

 aumentare ancora il livello degli warnings?
Se non ricordo male, ci sono vari -Wqui, -Wla, -Wsopra, -Wsotto,
che NON sono inclusi in -Wall (evidentemente "all" non significa
proprio "all"...:-) ma possono essere abilitati singolarmente.  Non mi
ricordo, pero`, i loro nomi esatti; qui, quando usavamo regolarmente
gcc, avevamo standardizzato su "-Wall -pedantic", che e` ancora
quello che uso (con egcs) quando capita.
Alex

 utilizzo del metodo try&error...
Secondo me non si deve accostare un termine in qualche modo negativo (o
almeno io l'ho interpretato così, se intendevi diversamente allora come
non detto) come "artigianale" con il fatto di essere autodidatta.
Autodidatta significa semplicemente che invece di aver avuto un
insegnante in carne ed ossa l'hai avuto 'virtuale' su un libro. E qual'è
la differenza? Un libro è sicuramente scritto meglio e meglio pensato di
quanto lo possa essere una lezione fatta in classe (specialmente con il
livello medio degli insegnanti che ci ritroviamo qui in Italia... :-( ),
non trovi? Inoltre, il metodo trial & error è sicuramente positivo in
molti aspetti dell'apprendimento e anche dello sviluppo di applicazioni
ma certo non deve sostituire una solida preparazione teorica di base.
EMG

visto che non sono mai troppo sicuro di quello che faccio ;-)
Io uso quasi sempre /W3 (o in gcc, -Wall -pedantic), cioe`, "abbastanza"
certosino ma non *totalmente* rompiscatole (i warning di livello piu` elevato
tendono secondo me a far perdere piu` tempo che non risparmiarne, o
almeno cosi` e` nella mia esperienza in merito; in un thread parallelo si
sta discutendo la stessa questione, sia qui sia in it.comp.lang.c).
Alex