Pregunta

Estoy trabajando en un pequeño juego roguelike, y para cualquier objeto / "cosa" que no es una parte del mapa se basa de una clase XEntity. Hay varias clases que dependen de ella, como XPlayer, XItem y XMonster.

Mi problema es que quiero convertir un puntero desde XEntity a XItem cuando sé que un objeto está en punto. El código de ejemplo que estoy utilizando para recoger un objeto es esto, es cuando una entidad diferentes picos de hasta un elemento que está de pie.

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;
    }
}

Lo primero que pensé fue crear un método en el que XEntity vuelve a sí mismo como un puntero XItem, pero crea dependencias circulares que son irresolubles.

Estoy bastante perplejo acerca de éste. Cualquier ayuda es muy apreciada.

¿Fue útil?

Solución

Si sabe que el XEntity es Actuall y XItem continuación, puede utilizar un molde estático.

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

Sin embargo, usted debe revisar a diseñar y ver si se puede operar en la entidad de manera que los medios que usted no necesita saber lo que deriva tipo es. Si se puede dar la clase base de una interfaz suficientemente rica que puede ser capaz de eliminar la inspección tipo de comprobación bandera.

Otros consejos

casting resuelve el problema como otros han señalado:

// 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.
}

Sin embargo creo que sould un paso atrás y mirar el diseño del programa, como downcasting es algo que debe y puede evitarse mediante un diseño adecuado orientado a objetos. Una poderosa, aunque un poco compleja, la herramienta podría ser la Visitante patrón .

Yo solía creer que downcasting siempre era posible evitar con un diseño "adecuado". Esto simplemente no es el caso, sin embargo. Un adecuado diseño muy a menudo tiene que tener sub-objetos que implementan el comportamiento nuevo, y no sólo diferente. Con demasiada frecuencia, los defensores de diseño "adecuado" le dirá a mover el nuevo comportamiento de la pila de la abstracción a lugares donde no pertenece. No siempre, pero si se mantiene tratando de asegurarse de que todas las clases se pueden utilizar desde el punto más abstracto que es muy a menudo cuando las cosas terminan yendo y es sólo fugly.

Una gran manera de hacer frente a downcasting de forma centralizada es usando el patrón del visitante. Hay varias formas de visitante embargo, algunos requieren downcasting, algunos no lo hacen. El visitante acíclico, el que requiere downcasting, es más fácil trabajar con él y es, en mi experiencia, más potente.

Otro visitante que no he intentado trabajar con reclamos para alcanzar la misma flexibilidad de visitante acíclico con la velocidad del visitante estándar; se llama "visitante cooperativa". Es todavía proyecta, sólo lo hace de una manera más rápida con su propia tabla de consulta. La razón por la que no he probado el visitante cooperativa es que no he encontrado una manera de hacer que funcione en múltiples higherarchies ... pero no he pasado mucho tiempo en ella, ya sea porque me he pegado a mí mismo (en mi proyecto actual) con acíclica.

La verdadera cosa fresca sobre el visitante cooperativa es tipos de retorno. Sin embargo, yo uso mis visitantes a visitar bloques enteros de objetos y hacer cosas con ellos. Tengo problemas de previsión de cómo un retorno funcionaría en estos casos.

Los visitantes downcasts estándar también que sólo lo hace a través del mecanismo de llamada virtual, que es más rápido ya veces más seguro que una conversión explícita. Lo que no gusta de este visitante es que si usted tiene que acudir WidgetX en el higherarchy Widget entonces también tiene que implementar la funcionalidad de la visita () para WidgetY y WidgetZ a pesar de que no se preocupan por ellos. Con higherarchies grandes y / o anchos esto puede ser un PITA. Otras opciones no requieren esto.

También hay un "visitante higherarchal". Se sabe cuándo dejar de fumar.

Si usted no está inclinado a usar un visitante sin embargo y deseo de simplemente fundido entonces es posible considerar el uso de la función de impulso :: polymorphic_downcast. Se cuenta con los mecanismos de seguridad y advertencia del elenco dinámico con afirma en versiones de depuración, y la velocidad de vaciado estático en un comunicado. Puede que no sea aunque sea necesario. A veces se acaba de saber que usted está lanzando derecha.

Lo más importante que hay que pensar y lo que se quiere evitar, está rompiendo el LSP. Si usted tiene un montón de código con "si (widget-> Tipo () == tipo 1) {abatido ...} else if (widget-> Tipo () == tipo2) ...", entonces la adición de nuevos tipos de controles es un gran problema que afecta a una gran cantidad de código de mala manera. El nuevo widget realidad, no será un widget, ya que todos sus clientes son demasiado íntima con su higherarchy y no saben al respecto. El patrón de visitante no deshacerse de este problema, pero lo hace centralizar, lo cual es muy importante cuando se tiene un mal olor, y que a menudo hace que sea más fácil de manejar en la parte superior de la misma.

Justo lo echó:

XItem* Item = (XItem*)Ent;

Un mejor enfoque, en general, es la siguiente:

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

Para que eso funcione, necesita habilitar RTTI. Mirada aquí para obtener más información.

A medida que han sido contestadas, hay 2 operadores:

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

Y:

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

la segunda es más lento pero más seguro (se comprueba si es posible) y podría devolver null, incluso si no es Ent.

I tienden a utilizar tanto envuelto en un método:

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
}

De esta manera consigo la comprobación de tipos mientras que el desarrollo (con una excepción si algo va mal) y me da la velocidad cuando se haya terminado. Puede utilizar otras estrategias si lo desea, pero debo admitir que me gusta bastante esta manera:)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top