Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Salve a tutti.
Stò lavorando ad un bel progettone ed ho bisogno di un'estrema malleabilità delle classi, perciò ho ben pensato di lavorare sulle classi virtuali come base di altre classi attaccate ad esse. E' andato tutto bene finché mi sono ritrovato davanti al problema nel passare un array di classi derivate in una funzione che avrebbe gestito la classe virtuale, padre delle classi figlie che gli avevo appena passato. Risultato? I membri sono completamente deallineati. Posto un esempio estremamente semplificato di quello che stò cercando di fare:
I membri della classe Base saranno spostati in base alla grandezza totale dei membri in più nella classe Blah. E' quasi ovvio, perché quando si passa al primo elemento dell'array, lui prende la dimensione della classe e la moltiplica per l'indice, ottenendone la posizione. Quindi se la classe Base è di 12 byte e Blah è di 20, in un array di tipo Blah usato come tipo Base, il primo elemento sarà all'offset 0, il secondo però sarà all'offset 12 invece che 20, il terzo sarà al 24 invece che al 40 e così via, disperdendo il valore reale delle variabili. Spero di essere stato il più chiaro possibile . Aspetto vostre idee e pareri
Ultima modifica effettuata da xeeynamo il 10/07/2011 alle 12:28
Beh, mi sembra che non ci sia un "errore" vero e proprio ma che sia normale ...
A parte il problema "didattico", non capisco dove tu voglia arrivare dato che non ha senso scrivere quel codice ...
Se proprio lo volessi fare funzionare, basterebbe correggere il (normale) comportamento del compilatore nel calcolare gli offset dei membri tra i puntatori di diverso tipo.
Codice sorgente - presumibilmente C/C++
void PrintParams(Base *base, size_t arraySize)
{
int offs = sizeof(Blah)-sizeof(Base);
for(unsigned int i=0; i<arraySize; i++)
PrintParam((Base *)(((char *)&base[i])+i*offs));
}
Un errore invece c'è (e potrebbe essere più grave) ... Alla fine la delete dovrebbe essere
Codice sorgente - presumibilmente Plain Text
delete [] asd;
dato che si riferisce ad un array di oggetti e che i distruttori di *tutti* gli elementi devono essere chiamati.
Ultima modifica effettuata da nessuno il 10/07/2011 alle 13:32
Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
Beh, mi sembra che non ci sia un "errore" vero e proprio ma che sia normale ...
A parte il problema "didattico", non capisco dove tu voglia arrivare dato che non ha senso scrivere quel codice ...
Se proprio lo volessi fare funzionare, basterebbe correggere il (normale) comportamento del compilatore nel calcolare gli offset dei membri tra i puntatori di diverso tipo.
Codice sorgente - presumibilmente C/C++
void PrintParams(Base *base, size_t arraySize)
{
int offs = sizeof(Blah)-sizeof(Base);
for(unsigned int i=0; i<arraySize; i++)
PrintParam((Base *)(((char *)&base[i])+i*offs));
}
Ciò che sto creando sarà strutturato in maniera estremamente modulare, per esempio ho la classe Picture, classe virtuale padre di classi figlie come BMP, GIF, DDS e così via. Col tuo sistema, ogni volta che aggiungo un "formato", dovrei prima di tutto aggiornare la classe Picture, idem se volessi rimuoverlo, quando preferirei qualcosa di completamente automatico. Però ti ringrazio perché ho trovato un'idea forse risolutiva: se ad esempio carico una BMP, farò sizeof(BMP)-sizeof(Picture) e la memorizzerò in Picture, poi farò una funzione chiamata GetPicture(int i) che partirà dal primo elemento dell'array e ricaverà l'indirizzo dell'elemento selezionato dall'indice i. Lo so che il compilatore non fa altro che comportarsi normalmente, di fatti non ho parlato di errori, ma mi stavo mangiando la testa perché magari mi era sfuggita una soluzione più semplice.
Testo quotato
Postato originariamente da nessuno: Un errore invece c'è (e potrebbe essere più grave) ... Alla fine la delete dovrebbe essere
Codice sorgente - presumibilmente Plain Text
delete [] asd;
dato che si riferisce ad un array di oggetti e che i distruttori di *tutti* gli elementi devono essere chiamati.
Sì, ma intendevo dire che non dovresti trovarti nelle condizioni di cui parli.
Se crei una classe Picture e poi da questa derivi i vari formati particolari, perché dovresti avere i problemi che ti sei "creato" nel codice d'esempio che hai mostrato?
Prova a scrivere le classi effettive che andrai ad utilizzare e mostrare dove trovi il problema ...
Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
http://www.2shared.com/file/ECuzzpc2/blahblah.html qui ho messo parte del sorgente. I commenti come "BLAH" sono parti del codice rimosse, inutili perché non c'entrano niente col problema . La classe Image.h è la classe virtuale dove si appoggiano i due formati IMGD e TM2 (texture della PS2). Le due classi IMGD e TM2 inoltre si appoggiano alla classe File, che si occuperà dell'apertura dei file nonché dei messaggi di errore.WinBridge.h sostituirà le classiche funzioni dello standard C come fopen, malloc ecc con le API di Windows. D3D9Texture sarà la classe che creerà le texture partendo dalla classe Image (quindi indipendentemente dal formato di immagine caricato, la texture verrà creata e visualizzata correttamente) che chiamerà la funzione interna della libreria D3D9 CreateTexture dalla funzione RenderModel per creare le texture del modello 3D ed applicarle. Ovviamente il codice funziona alla perfezione, ma solo se vi è una texture nel modello 3D. Il framework dovrà essere in grado di caricare diversi formati di modelli 3D con diversi tipi di texture, ecco perché ho bisogno di fare tutti questi giri tra classi virtuali etc . E' la prima volta che programmo usando così tante classi (quelli del link sono 16 dei 50 file di tutto il progetto) e non so esattamente come si gestiscono più formati, quindi se magari ho sbagliato qualcosa oppure ho progettato male sin dall'inizio la struttura del progetto, gradirei consigli da una persona sicuramente più esperta =)
I risultati stampati sono uguali. Praticamente, ogni classe sia virtuale sia figlia, dovrà avere un Get(int i) dove i stà per indice. Dovrà sempre essere usato il primo elemento dell'array (quindi asd[0].Get(i) oppure asd->Get(i)) e ritornerà la posizione corretta della classe, grazie ad un semplice "return this+i". Spero che la soluzione possa essere d'aiuto a qualcuno in futuro
Beh, è ovvio che con un metodo specifico delle due classi risolvi !
Ogni metodo "sa" come è fatta la propria classe e quindi gestisce correttamente l'offset dei membri.
Quello che mi sembrava tu volessi, fosse "accedere in memoria" con dei puntatori con un sistema che valesse sia per la classe base che per quella derivata.
Non ci siamo capiti ...
Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
Beh, è ovvio che con un metodo specifico delle due classi risolvi !
Ogni metodo "sa" come è fatta la propria classe e quindi gestisce correttamente l'offset dei membri.
Quello che mi sembrava tu volessi, fosse "accedere in memoria" con dei puntatori con un sistema che valesse sia per la classe base che per quella derivata.