Newsgroup: it.comp.lang.c++
Titolo: std::vector di classi derivate

Salve a tutti... ultimamente vi sto scocciando parecchio, eh! E
pensare che credevo di sapere molto sul c++!
Comunque...
Sto facendo un gioco in cui c'č una classe TMap che diciamo che
comprende tutto il gioco e, tra le altre cose, ha due membri
dichiarati in questo modo:
std::vector<TDynamicObject> vecOggettiDinamici;
std::vector<TActor> vecAttori;
Gli oggetti dinamici sono caricati da file e sono definizioni di
oggetti dinamici. Mettiamo, ad esempio, guerrieri, cavalli, frecce,
sassi scagliati, ecc.
Nel vettore vecOggettiDinamici ci metto i tipi di oggetti (sasso,
freccia, guerriero, cavallo), mentre nel vettore vecAttori ci metto
gli attori veri e propri contenuti nella mappa, quindi guerriero1,
guerriero2, sasso120, frecciascagliatadalguerriero1sulcavallo2, ecc.
Il fatto č questo: come conviene fare per gestire oggetti diversi?
Io pensavo di fare classi derivate di TDynamicObject... ma poi la
parte "in piů" di ogni oggetto (che sarŕ velocitŕ per una freccia,
caratteristiche di combattimento per un guerriero e cosě via... magari
anche una funzione tipo CalcolaMossaSuccessiva, variabile per ogni
tipo di classe, ecc.) č accessibile?
Se faccio vecOggettiDinamici[2], questo mi restituisce un oggetto
TDynamicObject... dovrei fare un cast? tipo
(TGuerriero) vecOggettiDinamici[2].CaratteristicaDiCombattimento.
Oppure no?
Un casting statico di questo genere mi comporta rallentamenti?
Oltretutto nel vettore vecAttori, gli oggetti hanno un riferimento
all'oggetto dinamico che rapporesentano, piů le informazioni che
servono alla funzione di disegno per disegnarli (ad esempio le
coordinate attuali nella mappa)...
Spero di essermi spiegato... voi come gestireste una cosa cosě?
Forse l'ho resa complessa ma in realtŕ č semplicissima, vero?
MadMage


Ad esempio:
class TDynamicObject
{
    public:
        void draw() = 0;
};
class Guerriero: public TDynamicObject
{
    public:
        void draw() { //... }
    //...  membri per gestire correttamente draw
};
Manlio Perillo

No, no, TROPPO overhead... i cast dinamici in un gioco non si sono mai
visti!!!
:-(

 (TGuerriero) vecOggettiDinamici[2].CaratteristicaDiCombattimento.
Come conseguenza di ciò che ho detto sopra, operator[]() di vector<T> non
può restituire un oggetto che abbia senso castare a qualcosa di diverso da
T, perchè è un T. Attenzione: il metodo push_back() di vector, ad esempio,
richiedendo un T& come argomento, accetta automaticamente anche reference
a classi derivate da T, ma l'oggetto che verrà creato nel vettore tramite
costruttore-copia sarà di tipo T. Se T è una classe astratta o
parzialmente astratta, std::vector<T> non è qualcosa che si possa
instanziare, mentre std::vector<T*> sì.
Non capisco l'uso che vuoi fare del cast.
Mi sembra di aver capito che tu voglia derivare le varie classi che
rappresentano gli oggetti del gioco da TDynamicObject. Il che sembrerebbe
suggerire che tu voglia sfruttare il polimorfismo. Se però il codice
client deve accedere a interfacce più specializzate e invocare metodi non
appartenenti a TDynamicObject, occorre che tu disponga di un meccanismo
per sapere che tipo di interfacce l'oggetto disponga e fare un
downcasting, che di fatto vuol dire usare dynamic_cast<> o qualche sistema
casalingo equivalente.
Max

 ha scritto nel messaggio
news:39f2b4c0...@news.libero.it...

Allora temo che hai sbagliato approccio.
E poi dynamic_cast non è TROPPO overhead.
Dipende dal contesto, da come è usato
(e da che compilatore usi).
Manlio Perillo

Oppure no?
In generale i cast doverebbero essere evitati e sono tranne
qualche caso particolare un indicatore di un probabile errore
di design dell'applicazione. Quasi sempre la presenza di un
cast o di uno switch() sul tipo di un oggetto indica in realta'
la necessita' di un metodo virtuale.
HTH
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

casalingo equivalente.
per downcasting intendi castare ad una classe derivata?
MadMage

la necessita' di un metodo virtuale.
č piů veloce un cast o l'utilizzo di un metodo virtuale?
MadMage

è più veloce un cast o l'utilizzo di un metodo virtuale?
Non e' questione di velocita'. Tutte le volte che usi un
downcasting stai vincolando il programma alle classe
derivate attualmente. Questo significa che molto probabilmente
dovrai aggiornare questi downcast nel caso di aggiunta di
una nuova classe derivata.
Il bello delle funzioni virtuali e' che chiamano la versione
corretta della funzione anche se il codice di chiamata era
stato scritto o addirittura compilato prima che la classe
derivata venisse concepita.
Downcasting e switch() sul tipo di oggetto sono chiamati
"code to find code" e sono una delle pratiche che aumentano
i costi di manutenzione del software e che la programmazione
object oriented permette di eliminare.
Se comunque proprio ti interessa la velocita' il downcasting
statico nel caso che non venga utilizzata inheritance virtuale
o multipla dovrebbe essere a mio parere una operazione a costo
NULLO. In altre parole non cambiano i bit del puntatore, ma solo
quello che il compilatore ti permette di fare con il puntatore
stesso (quindi le uniche vere operazioni di cast sono compiute
compile time). Nota pero' che dopo un downcasting non hai ancora
effettuato la chiamata al metodo.
Il confronto e' quindi fra una chiamata statica e una chiamata
con late binding.
In effetti le chiamate di metodi virtuali hanno un overhead
che pero' e' a mio parere paragonabile al test che hai dovuto
fare per sapere quale fosse la classe dell'oggetto prima di
poter effettuare il downcasting.
Il discorso cambia nel caso di funzioni particolarmente
semplici che se chiamate staticamente possono esser compilate
inline con notevole risparmio. In particolare se dopo un solo
test tu fai un loop di chiamate statiche.
Personalmente non ho mai avuto problemi di prestazioni che
fossero impotabili a lookup della VMT (anche se devo dire
che su particolari classi sto attento a come progetto il
codice relativo).
Tieni soprattutto presente che in generale il primo passo
per ottimizzare e' misurare. Se non trovi tramite misurazione
che hai effettivamente un problema specifico io ti consiglierei
di scegliere l'alternativa piu' pulita (funzioni virtuali).


Dunque, facciamo che io metta
std::vector<TActor*> vecActors;
stavo comunque pensando che la parte "in più" delle classi derivate
serve che sia accessibile solo dalla funzione
CalcolaMossaSuccessiva().
Quindi, se TActor possiede questa funzione virtuale, posso dunque fare
for each actor in vecActors {
	actor->CalcolaMossaSuccessiva();
}
(in pseudocodice)
e all'interno di CalcolaMossaSuccessiva utilizzare le variabili e le
funzioni aggiunte della classe derivata.
class TPippo : public TActor {
public:
	CalcolaMossaSuccessiva();
	int lunghezzapiededestro, lunghezzapiedesinistro;
}
void TPippo::CalcolaMossaSuccessiva()
{
	mossasuccessiva = lunghezzapiededestro +
lunghezzapiedesinistro;
}
Giusto?!?!?!?
mi conviene dichiarare la funzione CalcolaMossaSuccessiva in TActor
come:
1) virtual void CalcolaMossaSuccessiva();
2) virtual void CalcolaMossaSuccessiva() = 0;
3) void CalcolaMossaSuccessiva() {};
??? Intendo in termini di minor overhead possibile.
Grazie, 'sto ng è veramente formidabile!!!
MadMage


e perché non
for (int i = 0; i < vecAttori.size(); i++)
	vecAttori[i]->CalcolaMossaSuccessiva
????????
MadMage

 ha scritto nel messaggio
news:39f3513c...@news.libero.it...

Credo che sbagli a pensare una cosa del genere.
Supponi di avere una :
TActor* pl;
TGuerriero* g;
//...
if(g = dynamic_cast<TGuerriero*>(pl)
{
    // pl è un guerriero
    g->mossa_successiva();
};
Se la funzione mossa_sucessiva() richiede 'del' tempo
per essere terminata (ad esempio deve aggiornare una scena
3D), che vuoi che importi del tempo impiegato da dynamic_cast??
Per contesto mi riferivo a questo.
Una funzione non è costosa in *assoluto*.
Devi sempre comparare l'overhead con qualcos'altro.
Comunque, come ti hanno già suggerito, in casi come questi
è *molto* meglio usare funzioni virtuali.
Tuttavia credo che dynamic_cast possa essere comunque utile
in alcuni casi.
Infatti supponiamo che tu abbia una classe base tipo
TActor;
A questo punto da TActor potresti derivare altre classi descriventi
attori con proprietà distinte:
Ad esempio:
class TAnimale: public TActor;
class TGuerriero: public TActor;
class TCivile: public TActor;
Queste tre classi potrebbero avere caratteristiche così
diverse che non sarebbe fattibile usare le stesse
funzioni virtuali.
Così in TActor dovresti inserire i metodi fondamentali a tutti
(implementati come funzioni virtuali pure).
Poi nelle tre sottoclassi dovresti aggiungere metodi
virtuali descriventi loro caratteristiche specifiche
(e possono anche essere virtuali pure).
In questo modo crei una gerarchia di classi
(i personaggi concreti sono derivati da queste tre sottoclassi
fondamentali).
A questo punto se in una certa parte del programma devi sapere
a che sottocategoria appartiene un TActor*, devi usare dynamic_cast.
Questo significa però che se aggiungi una nuova sottoclasse
fondamentale (tipo TAnimale), devi modificare il codice.
Tuttavia credo che queste sottoclassi fondamentali possano
essere scelte una volta per tutte e difficilmente ci sia
la necessità di aggiungerne di altre.
Manlio Perillo