Domanda

Sto lavorando su un piccolo gioco simile a Rogue, e per qualsiasi oggetto / "cosa" che non è una parte della mappa è basata fuori una classe XEntity. Ci sono diverse classi che dipendono da esso, come ad esempio XPlayer, XItem e XMonster.

Il mio problema è che voglio convertire un puntatore da XEntity a XItem quando so che un oggetto è in oggetto. Il codice di esempio che sto usando per raccogliere un oggetto è questo, è quando un ente diverso raccoglie un elemento che è in piedi sopra.

void XEntity::PickupItem()
{
    XEntity *Ent = MapList; // Start of a linked list

    while(true)
    {
        if(Ent == NULL) { break; }

        if(Ent->Flags & ENT_ITEM)
        {
            Ent->RemoveEntity(); // Unlink from the map's linked list

            XItem *Item = Ent // Problem is here, type-safety

            // Code to link into inventory is here

            break;
        }

        Ent = Ent->MapList;
    }
}

Il mio primo pensiero è stato quello di creare un metodo in XEntity che si ritorna come un puntatore XItem, ma crea dipendenze circolari che sono irrisolvibili.

Sono abbastanza perplesso su questo. Ogni aiuto è molto apprezzato.

È stato utile?

Soluzione

Se so che il XEntity è actuall e XItem quindi è possibile utilizzare un cast statica.

XItem* Item = static_cast<XItem *>(Ent);

Tuttavia, si dovrebbe rivedere a progettare e vedere se è possibile operare sul soggetto in un modo che significa che non c'è bisogno di sapere che cosa deriva tipo è. Se si può dare la classe di base sufficientemente ricca interfaccia può essere in grado di eliminare l'ispezione tipo di controllo bandiera.

Altri suggerimenti

Casting risolve il problema come altri hanno sottolineato:

// dynamic_cast validates that the cast is possible. It requires RTTI 
// (runtime type identification) to work. It will return NULL if the 
// cast is not possible.
XItem* Item = dynamic_cast<XItem*>(Ent);
if(Item)
{
    // Do whatever you want with the Item.
}
else
{
    // Possibly error handling code as Ent is not an Item.
}

Tuttavia penso che si Sould passo indietro e guarda alla progettazione del programma, come downcasting è qualcosa che dovrebbe e può essere evitato da una progettazione orientata agli oggetti corretta. Un potente, anche se un po 'complessa, strumento potrebbe essere la href="http://en.wikipedia.org/wiki/Visitor_pattern" modello Visitor .

Ho usato a credere che downcasting è sempre stato possibile evitare con un design "corretta". Questo semplicemente non è il caso però. Una corretta progettazione molto spesso ha bisogno di avere sotto-oggetti che implementano nuovi, e il comportamento non solo diverso. Troppo spesso i sostenitori del progetto "propria" si dirà di spostare il nuovo comportamento lo stack di astrazione in luoghi in cui non appartengono. Non sempre, ma se continui a cercare di assicurarsi che tutte le classi possono essere utilizzate dal punto più astratta questo è molto spesso dove le cose finiscono per andare ed è solo fugly.

Un ottimo modo per trattare con downcasting in modo centralizzato è quello di utilizzare il modello Visitor. Ci sono diverse forme di visitatore, però, alcuni richiedono downcasting, alcuni non lo fanno. Il visitatore aciclica, quella che richiede downcasting, è più facile da lavorare ed è, nella mia esperienza, più potente.

Un altro visitatore che non ho cercato di lavorare con i reclami per soddisfare la stessa flessibilità di visitatore aciclico con la velocità del visitatore di serie; si chiama "visitatore cooperativa". Si getta ancora, lo fa solo in un modo più veloce con essa la propria tabella di ricerca. Il motivo per cui non ho provato il visitatore cooperativa è che non ho trovato un modo per farlo funzionare su più higherarchies ... ma non ho speso un sacco di tempo su di esso o perché mi sono bloccato (nel mio progetto in corso) con aciclico.

La vera cosa più cool il visitatore cooperativa è tipi restituiti. Comunque, io uso i miei visitatori a visitare interi blocchi di oggetti e fare le cose con loro. Ho difficoltà a immaginare come un ritorno avrebbe funzionato in questi casi.

Il visitatore di serie downcasts anche solo fa attraverso il meccanismo di chiamata virtuale, che è più veloce e, a volte più sicuro di un cast esplicito. La cosa che non mi piace di questo visitatore è che se avete bisogno di visitare WidgetX nel higherarchy Widget poi si deve anche implementare visita () funzionalità per WidgetY e WidgetZ anche se non si cura di loro. Con grandi e / o larghi higherarchies questo può essere una valle di lacrime. Altre opzioni non richiedono questo.

C'è anche un "visitatore higherarchal". Si sa quando smettere.

Se non siete inclini a usare un visitatore e anche se vuole lanciare solo allora si potrebbe considerare l'utilizzo della funzione di boost :: polymorphic_downcast. Ha i meccanismi di sicurezza e di pericolo di fusione dinamica con asserisce nella build di debug, e la velocità del cast statico in un comunicato. Potrebbe non essere necessario, però. A volte basta sapere che si sta gettando a destra.

La cosa importante che dovete pensare e ciò che si vuole evitare, sta rompendo il LSP. Se si dispone di un sacco di codice con "if (widget-> digitare () == tipo 1) {abbattuto ...} else if (widget-> tipo () == tipo2) ...", quindi l'aggiunta di nuovi tipi di widget è un grande problema che colpisce un sacco di codice in un brutto modo. Il tuo nuovo widget non sarà davvero un widget, perché tutti i tuoi clienti sono troppo intimo con il vostro higherarchy e non sanno su di esso. Il modello visitatore non sbarazzarsi di questo problema ma non centralizzare, che è molto importante quando hai un cattivo odore, e rende spesso più semplice trattare con su di esso.

Proprio cast:

XItem* Item = (XItem*)Ent;

Un approccio migliore, nel complesso, è questa:

if (XItem *Item = dynamic_cast<XItem*>(Ent)) {
    Ent->RemoveEntity();

    // Code to link into inventory is here

    break;
}
XItem * Item = dynamic_cast< XItem * >( Ent );

if ( Item )
    // do something with item

Al fine di che lavorare, è necessario attivare RTTI. Guardate qui per ulteriori informazioni.

Come sono state esaudite, ci sono 2 operatori:

XItem* Item = static_cast<XItem*>(Ent);

E

XItem* Item = dynamic_cast<XItem*>(Ent);

il secondo è più lento ma più sicuro (controlla se è possibile) e potrebbe restituire nulla, anche se non è Ent.

tendo ad utilizzare sia avvolto in un metodo:

template <class T, class U>
T* my_cast(U* item)
{
#ifdef _NDEBUG_
  if (item) return &dynamic_cast<T&>(*item); // throw std::bad_cast
  else return 0;
#else
  return static_cast<T*>(item);
#endif
}

In questo modo ottengo il controllo di tipo mentre lo sviluppo (con un'eccezione se qualcosa va male) e ottengo velocità quando ho finito. È possibile utilizzare altre strategie, se lo si desidera, ma devo ammettere che mi piace molto questo modo:)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top