|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
C++: Questa proprio non la sapevo
Guardate questo codice:
#include <iostream.h> class C1 { virtual void print () { cout << "print() di C1\n"; } public: C1 () { print(); } void do_print () { print (); } }; class C2: public C1 { void print () { cout << "print() di C2\n"; } }; int main() { C2 C; C.do_print(); return 0; } L'output del programma è: print() di C1 print() di C2 ed è uguale sia sul g++ 2.91, g++ 2.96 che sul visual c. Succo: nei costruttori di classe le funzioni virtuali a quanto pare non sono usate come tali - qualcuno ne sa qualcosa di più a riguardo?
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Sinceramente non mi ricordo come funziona l'ereditariatà sul costruttore di default...in ogni caso sembra che tu abbia ereditato anche quello...
Infatti con "C2 C;" richiami il costruttore di default di C2 erediatato da C1 e poi con "do_print()" che è ereditata da C1 richiami la print() di C2 come è sicuramente giusto (visto che l'stanza è di tipo C2)... Comunque funziona allo stesso modo anche in VC++... |
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
No, non è così, il costruttore di C1 viene chiamato prima di quello di default di C2, è normale che sia così. Il problema è che a quanto sembra, C viene inizialmente costruito come C1, poi come C2. Solo questo può spiegare il fatto che, se il costruttore chiama una funzione virtuale, non viene chiamata quella effettiva della classe di appartenenza.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Nov 2000
Città: MILANO
Messaggi: 2662
|
.
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Nov 2000
Città: MILANO
Messaggi: 2662
|
la chiamata a print è fatta in una funzione di C1 e chiama un'altra funzione dello scope di C1. Quindi mi sembra normale che NON possa vedere un elemento funzione di C2.
facciamo un altro esempio che scrivo adesso sperando di non confonderti le idee: class A { public: A(); virtual print() {printf("classe A\n"}; }; class B public: print() { printf("classe B\n"); }; } int main() { A a; a.print(); B b; b.print(); } deve stamparti "classe A" e poi "classe B" e tutto funziona correttamente secondo le regole. Nel tuo esempio la funzione che ridefinisci innanzi tutto è privata e non può essere ridefinita perchè la classe derivata può accedere solo ai membri pubblc della super classe. In secondo luogo tu chiami esplicitamente una funzione della superclasse - NON della derivata - e che perciò non può " vedere " nello scope della derivata - ci mancherebbe - e chiama corettamente la funzione print() del SUO scope. Occhio perchè questo esempio è molto incasinato. Non si usa in questo modo il polimorfismo. Inoltre usa sempre i puntatori quando hai a che fare con il polimorfismo: altrimenti non serve a nulla. Ad es. tu sai che devi tenere un elenco di oggetti Sensor in un tuo programma ma non sai ancora se questi oggetti in particolare saranno dei TouchSensor o dei LightSensor ecc. Allora tieni un vettore di puntatori a Sensor e per ognuno a run time allocherai la giusta classe derivata. In questo modo, chiamando i metodi virtuali - stiamo parlando di metodi virtuali NON puri ovviamente - della classe Sensor, verranno in realtà invocati i corrispettivi metodi della derivata: Sensor* lista[10]; lista[0]= new LightSensor(); ecc. |
|
|
|
|
|
#6 | |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
|
#7 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Non capisco qual è il problema...la print di C1 è virtuale...quindi, visto che è definita anche la print di C2 nella vtable della classe è presente il puntatore alla funzione print di C2...
|
|
|
|
|
|
#8 |
|
Bannato
Iscritto dal: Jul 2000
Città: Malo (VI)
Messaggi: 1000
|
Anche a me e' accaduto un problema simile,
solo che la funzione nella classe base non era definita, e quindi il programma crashava con un "Pure virtual method called" o qualcosa di simile Probabilmente ogni costruttore provvede solamente ad aggiungere ( ovvero sovrascrivere ) nella virtual table i metodi ridefiniti. QUando sei nel costruttore della classe base di conseguenza, non hai nessun metodo ridefinito. Perche' non hanno deciso di scrivere subito la virtual table completa ? Secondo me l'hanno fatto per poter riutilizzare i costruttori delle classi basi senza doverli spezzare in piu' parti, a vantaggio dell'efficienza. ( Anche se quest'ultima cosa e' solo una mia ipotesi ). |
|
|
|
|
|
#9 | |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
|
#10 | |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
|
#11 |
|
Bannato
Iscritto dal: Jul 2000
Città: Malo (VI)
Messaggi: 1000
|
Cerco di spiegarmi meglio:
La 'costruzione' di un oggetto richiede vari cose giusto ( 'aggiustamento' )della vtable esecuzione del costruttore definito dall'utente e cosi' via ). Ora se io volessi avere da subito la virtual table "esatta", dovrei riuscire a effettuare solo parte di queste operazioni, in particolare escludere la scrittura della vtable da parte dei costruttori derivati, ( perche' altrimenti la stessa tornerebbe 'carente' ). E' probabile che questo sia di difficile esecuzione: se pensi che potrei aver derivato un oggetto che mi arriva da una libreria esterna, questo vuol dire che ogni libreria dovrebbe esportare una certa quantita' di funzioni ( o comunque di informazioni ) da chiamare in caso di derivazione. |
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
mmm questo mi sembra già più convincente, anche se continuo ad avere dei dubbi sul fatto che modi alternativi siano impossibili da gestire
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
#13 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Posso provare a dare una spiegazione...un po' a occhio...
Implementando il costruttore di default per C2 viene lo stesso richiamato il costruttore di default di C1... Quindi al momento della chiamata del costruttore di C1 si tratta di costruire un oggetto di tipo C1...la cui vtable verrà poi sovrascritta dalla creazione dell'oggetto C2 che viene derivato da C1 di cui eredita la parte pubblica... Quindi secondo me il compilatore opera in questo modo... Crea un'istanza di C1 e ne chiama il costruttore (a questo punto l'oggetto è ancora di tipo C1...ed è per questo che viene chiamata la print di C1)...l'istanza di C2 eredita i membri e le funzioni pubbliche di C1...viene modificata la vtable (viene sovrascritta l'entry di print())...poi successivamente viene chiamato il costruttore di C2... Modificando il codice in questo modo si vede che quello che avviene sembra essere questo... #include <iostream.h> class C1 { virtual void print () { cout << "print() di C1\n"; } public: C1 () { print(); } void do_print () { print (); } }; class C2: public C1 { void print () { cout << "print() di C2\n"; } public : C2 () {print();}; }; int main() { C2 C; C.do_print(); return 0; } |
|
|
|
|
|
#14 |
|
Senior Member
Iscritto dal: Nov 2000
Città: MILANO
Messaggi: 2662
|
MA RAGAZZI!!! ho capito adesso la domanda. MA NON SCHERZIAMO!! quando ereditate una classe VIENE SEMPRE CHIAMATO IL COSTRUTTORE DELLA SUPERCLASSE se no non funzionerebbe più niente. E che senso avrebbe?
per IL_sensine: non sono sicuro di come funzioni in questo caso. ripasserò un po'. comunque se chiamate q.sa che sta solo nella superclasse questo NON può vedere la parte 'aggiunta ' ereditando. |
|
|
|
|
|
#15 | |||
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
Quote:
Quote:
Il problema (che cionci e /\/\@®¢Ø) hanno intuito) a quanto pare è che la vtable viene costruita mano mano che vengono chiamati i costruttori delle classi, e non _prima_ come mi sarei aspettato. Tutto qui.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|||
|
|
|
|
|
#16 |
|
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Certo...
Al momento della chiamata del costruttore di C1 l'istanza è a tutti gli effetti della classe C1...poi successivamente viene modificata la vtable al momento dell'allocazione di C2... |
|
|
|
|
|
#17 | |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
|
#18 |
|
Senior Member
Iscritto dal: Nov 2000
Città: MILANO
Messaggi: 2662
|
allora non avevo capito. forse perchè questo mi sembrava più ovvio mentre in realtà mi sono accorto che non mi ricordo bene altri aspetti a dire il vero
|
|
|
|
|
|
#19 |
|
Senior Member
Iscritto dal: Nov 2000
Città: MILANO
Messaggi: 2662
|
AAAAAHHHH adesso ho capito ancora meglio! in sostanza il problema è che secondo voi non doveva invocare la print di C1 durante l'invocazione al costruttore, perchè questa è già stata ridefinita. Mhhh beh quando viene chiamato il costruttore della super classe - vale anche per una catena di ereditarietà - l'oggetto modifica tipo man mano: se C eredita da B che eredita da A, quando si crea un oggetto C, prima viene costruito un oggetto A poi esteso a B poi esteso a C. quindi mi sembra coerente che durante l'esecuzione del costruttore di C1 chiami ancora la print di C1. Non so se questo in effetti sia un limite anzi. Direi che se si cercasse di fare veramente una cosa come nell'esempio con quell'intento, sarebbe sbagliata la scelta di ereditare C2 da C1 - non sto criticando chi lo ha scritto
|
|
|
|
|
|
#20 | ||
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
||
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 09:23.


















