2013-07-01 22 views
7

Data una classe base di gameObject e una classe derivata di animatedGameObject, ho pensato che potrebbe essere utile archiviare tutte le loro istanze in un std::vector. Se il vettore GameObjects è dichiarato come il tipo di base di gameObject*, le istanze dell'oggetto derivate richiedono il cast.Esistono rischi di prestazioni per l'utilizzo di static_cast per gestire un vettore di oggetti misti (di base e derivati)? (alias "è un'idea stupida?")

Esempio:

vector<gameObject*> GameObjects; 

gameObject A* = new gameObject(...init...); 
animatedGameObject B* = new animatedGameObject(...init...); 

GameObjects.push_back(A); 
GameObjects.push_back(B); 

// to access the animatedGameObject functions: 
static_cast<animatedGameObject*>(GameObjects[1])->functionUniqueToAnimated(); 

Aver paura come al solito, mi rivolsi a Scott Meyers (Effective C++, 3rd Edition), che scrive sul tema:

Molti programmatori credono che calchi non fare nulla ma chiedi ai compilatori di trattare un tipo come un altro, ma questo è sbagliato. Digitare conversioni di qualsiasi tipo (sia esplicite tramite cast o implicite dai compilatori) spesso portano al codice che viene eseguito in fase di runtime.

ho letto attraverso il suo Articolo 27: Ridurre al minimo Casting due volte, ma data la mia inesperienza con questo, sto lottando con la mia incapacità di rispondere a una semplice domanda "questa è una cosa stupida da fare?"

devo dire che ci sono diversi motivi per cui è una cosa stupida da fare che non hanno nulla a che fare con invocando static_cast. Le domande, in ordine di importanza, sono:

  1. Am non ho visto alcune possibili rischi con l'uso di static_cast nel mio esempio di cui sopra?
  2. Esistono strutture dati migliori rispetto allo std::vector per tali approcci? (Solo se c'è uno che è ovvio, non sto chiedendo di fare la mia ricerca per me.)

Questa è la mia prima volta facendo una domanda qui, quindi mi scuso in anticipo, se necessario.

+0

+1 per aver letto Effective C++ – Borgleader

+1

La solita cosa da fare qui sarebbe definire 'draw()' come una funzione virtuale nella classe base. Potresti quindi semplicemente chiamarlo senza calchi e farebbe la cosa giusta. Ne sei a conoscenza, ma vuoi sapere esattamente gli effetti del cast? – jogojapan

+1

'boost :: ptr_vector' – Mehrdad

risposta

7

static_cast non è lo strumento giusto per il lavoro, a meno che non si sappia che il puntatore va a animatedGameObject e non a gameObject. Quale struttura dati stai usando per memorizzare tali informazioni?

Determinazione del tipo di oggetto derivato dopo un puntatore di base è il lavoro di invio dinamico o dynamic_cast. Nel tuo esempio, la chiamata GameObjects[1]->draw() dovrebbe funzionare senza cast perché draw dovrebbe essere una funzione virtuale. Altrimenti puoi usare dynamic_cast< animatedGameObject & >(* GameObjects[1]) per affermare che l'oggetto è un oggetto animatoGoosoggetto e lanciare un'eccezione std::bad_cast se non lo è. (Questo sarebbe ancora bisogno di una funzione virtual in class gameObject, di solito il suo distruttore.)

Ma facendo static_cast ad un tipo derivato polimorfico è un odore di codice.


anche chiedere se std::vector è una buona struttura di dati per questo caso d'uso. Lo è, ma non un vettore di puntatori "nudi". C++ 11 ora fornisce classi di gestione della memoria "smart pointer" che eseguono per te new e delete, rendendo gli operatori attuali quasi obsoleti. Esamina std::unique_ptr per questo caso.

+1

Mi limiterò a un altro voto/maggiore visibilità per rendere 'draw' virtuale in modo che questo possa essere fatto senza casting. Questo è uno degli esempi * classici * di cose per le quali le funzioni virtuali funzionano essenzialmente alla perfezione. –

+1

@jogojapan D'oh, mi sono confuso. Un deleter personalizzato può rendere 'unique_ptr' gestire polimorficamente i tipi non polimorfici. Risolverà. – Potatoswatter

+0

Grazie mille. Andando a andare a leggere su 'std :: shared_ptr' – ilzxc

1
  1. Se gameObjects [1] non è animatoGameObject, l'applicazione (molto probabilmente) morirà in modo orribile.
  2. Se draw() è il metodo virtuale che è presente in gameObject, la conversione è inutile.

In generale, la trasmissione della classe base alla classe derivata non è sicura. In quelle situazioni ha senso usare dynamic_cast. Dynamic_cast restituisce NULL, se la conversione non può essere eseguita.

ci sono migliori strutture dati di

Beh, se la vostra gameObjects non vengono eliminati automaticamente altrove, potrebbe avere senso per usare qualcosa come std::vector<std::shared_ptr<gameObject> >. Tuttavia, i puntatori condivisi standard possono introdurre spese nascoste (extra new/delete, nel peggiore dei casi si potrebbe anche introdurre blocco mutex multithread, se sono progettati per essere thread-safe), così si dovrebbe assicurarsi che tali spese siano compatibili con il obiettivi.