2012-10-22 17 views
6

Sto costruendo un progetto di gioco semplice per un mio progetto. Ho le seguenti classi:Puntatore della trasmissione da tipo base a tipo figlio

class Character 
{ 
public: 
    virtual void Display(); 
    virtual void SetParameters(char* param, ...); 
}; 

class NonPlayableCharacter : public Character 
{ 

public: 
    virtual void Display(); 
    virtual void SetParameters(char* paaram, ...); 
    int GetNPCState(); 
} 

E poi ho un sacco di classi che derivano sia dal carattere o NonPlayableCharacter. Mi definisco in questo modo:

std::vector<Character*> _allChar; 

mio problema è che in un dato momento vorrei eseguire alcune operazioni su uno degli elementi del vettore. Quindi, ottenendo un elemento dal vettore, non posso chiamare direttamente il metodo GetNPCState() perché l'elemento nel vettore è di tipo Carattere *. Quindi:

_allChar[0]->GetNPCState(); 

non funziona. Così ho provato a fare con il famoso dynamic_cast:

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]); 
test->GetNPCState(); 

Il problema con questo ultimo tentativo è che GetNPCState() crash perché l'oggetto è nullo, e so per certo (tramite il debug) che _allChar [0] isn' t null.

+2

Si ottiene un puntatore nullo quando l'oggetto che si sta tentando di trasmettere in 'NonPlaya bleCharacter' non è in effetti un 'NonPlayableCharacter'. Probabilmente alcuni degli oggetti nel vettore sono derivati ​​direttamente da 'Character'. – jogojapan

+0

Come parte, io lavoro con un componente (s) che utilizza molti vettori di puntatori. Ha causato un sacco di guai per me, dato che questo vettore finisce a volte con dei puntatori alla memoria cancellata, e porta alla corruzione della memoria. Immagino che tu usi un vettore puntatore perché lo stai usando in modo polimorfico e/o gli oggetti sono condivisi.In entrambi i casi, consiglio vivamente di cambiare il vettore per tenere un tipo di puntatore condiviso ('std :: shared_ptr' per esempio) o cambiare il vettore in' boost :: ptr_vector' (che è ottimo per entrambi gli scenari). Buona fortuna con il gioco! – Dennis

risposta

6

Ci sono diversi tipi di calchi in C++ (4), di cui 2 sono di interesse qui:

  • static_cast presuppone che si sa cosa si sta facendo
  • dynamic_cast controlli, in fase di esecuzione , che si "indovinato" diritto

Nota: semplificata, come dynamic_cast permette anche incrociati calchi e getta involvi ng basi virtuali.

ci sono 3 versioni di dynamic_cast, in realtà, a seconda della natura del bersaglio:

  • se il bersaglio è un riferimento (cioè dynamic_cast<T&>(u)), quindi se il controllo non riesce dynamic_cast genera un'eccezione std::bad_cast
  • se il bersaglio è un puntatore (cioè dynamic_cast<T*>(p)), allora se i controlli fallisce dynamic_cast restituisce un puntatore nullo
  • infine, come caso particolare, se il bersaglio è void*, poi dynamic_cast invece restituisce l'indirizzo del pieno oggetto

In questo caso, è possibile:

  • passaggio da dynamic_cast<NonPlayableCharacter*>(_allChar[0])->getNPCState() a dynamic_cast<NonPlayableCharacter&>(*_allChar[0]).getNPCState(), e lasciare che l'eccezione si propaga
  • prova il risultato di il cast (NonPlayableCharacter* test qui) per la non nullità
3

dynamic_cast restituisce NULL se il cast è impossibile. Controlla cosa c'è dentro _allChar[0]. Si potrebbe fare funzione come getType() dove i rendimenti oggetto predefinito tipo id e quindi utilizzare static_cast:

if (_allChar[0]->getType() == TYPE_NO_PLAYER) { 
    static_cast<NonPlayableCharacter*>(_allChar[0])->getNpcState(); 
} 
5

Dopo

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]); 

si dovrebbe verificare se test è NULL. Anche se _allChar[0] non è NULL, dynamic_cast può restituire NULL se l'oggetto a cui punta non è un NonPlayableCharacter.

Così la versione corretta sarebbe:

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]); 
if (test) 
{ 
    test->GetNPCState(); 
} 
2

si deve testare la dynamic_cast per il successo. Esso restituisce un puntatore nullo in caso di errore:

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]); 
if (test) test->GetNCPState(); 

Il problema potrebbe essere che il primo elemento non punta a un oggetto NonPlayableCharacter.

2

dynamic_cast ritorna NULL quando il suo argomento non punta a un NonPlayableCharacter (quindi il primo elemento della matrice probabilmente punta a qualche altra sottoclasse di Character) - quindi è necessario verificare la presenza di NULL dopo il cast. Tuttavia, l'utilizzo di dynamic_cast potrebbe essere indicativo di un problema di progettazione. Forse dovresti invece avere un metodo virtuale su Character chiamato ad es. PerformMainActionInGameLoop() e viene sovrascritto in modo appropriato nelle diverse sottoclassi?

2

Per ottenere un puntatore figlio da un puntatore di base, è necessario utilizzare dynamic_cast.Il suo comportamento è la seguente:

  • Se il puntatore punta a un Child* allocata con new Child poi dynamic_cast<Child*> restituisce un Child*
  • Se il puntatore punta a qualcosa d'altro, poi dynamic_cast rendimenti NULL.

Il tuo problema è che non hai assegnato con new o il tuo oggetto è di un tipo diverso.

2

Probabilmente c'è una soluzione OO migliore per l'utilizzo di dynamic_cast, ma l'intero punto di utilizzo di questo cast è che restituirà un puntatore NULL se il cast fallisce.

Quindi controllare NULL prima di chiamare GetNPCState();

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]); 
if(test != NULL) 
{ 
    test->GetNPCState(); 
} 
Problemi correlati