Question

Je travaille sur un petit jeu de roguelike, et pour tout objet / « chose » qui ne fait pas partie de la carte est basé sur une classe de XEntity. Il existe plusieurs classes qui en dépendent, comme XPlayer, XItem et XMonster.

Mon problème est que je veux convertir un pointeur de XEntity à XItem quand je sais qu'un objet est au point. L'exemple de code J'utilise pour ramasser un article est cela, il est quand une autre entité prend un élément, il est debout au-dessus.

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

Ma première pensée a été de créer une méthode dans XEntity qui se retourne comme un pointeur XItem, mais il crée des dépendances circulaires qui sont sans solution.

Je suis assez perplexe au sujet de celui-ci. Toute aide est grandement appréciée.

Était-ce utile?

La solution

Si vous savoir que le XEntity est actuall et XItem vous pouvez utiliser une distribution statique.

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

Cependant, vous devriez vous revoir concevoir et voir si vous pouvez utiliser sur l'entité d'une manière qui signifie que vous ne savez pas besoin de ce dérivé de type il est. Si vous pouvez donner à la classe de base d'une interface suffisamment riche, vous pourrez peut-être éliminer l'inspection du type de contrôle du drapeau.

Autres conseils

Castings résout le problème comme d'autres l'ont souligné:

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

Cependant, je pense que vous Sould un pas en arrière et regardez la conception du programme, comme downcasting est quelque chose qui doit et peut être évité par une conception orientée objet approprié. Un puissant, même si un peu complexe, l'outil pourrait être le modèle de visiteur .

Je croyais que downcasting était toujours possible d'éviter avec une conception « correcte ». Ce n'est pas le cas cependant. Une bonne conception des besoins très souvent d'avoir des sous-objets qui mettent en œuvre de nouveaux, et pas seulement un comportement différent. Trop souvent, les défenseurs de la conception « correcte » vous dira de déplacer le nouveau comportement la pile d'abstraction à des endroits où il ne fait pas partie. Pas toujours, mais si vous continuez à essayer de vous assurer que toutes les classes peuvent être utilisées à partir du point le plus abstrait ce qui est très souvent là où les choses finissent par aller et il est juste fugly.

Une excellente façon de traiter de manière coulée en descente centralisée est en utilisant le modèle des visiteurs. Il existe plusieurs formes de visiteurs cependant, certains exigent downcasting, certains ne le font pas. Le visiteur acyclique, celui qui nécessite downcasting, est plus facile de travailler avec et est, dans mon expérience, plus puissant.

Un autre visiteur que je ne l'ai pas essayé de travailler avec les revendications pour répondre à la même flexibilité des visiteurs acyclique avec la vitesse du visiteur norme; il est appelé « visiteur coopérative ». Il jette encore, il fait juste d'une manière plus rapide avec sa propre table de consultation. La raison pour laquelle je ne l'ai pas essayé le visiteur coopérative est que je ne l'ai pas trouvé un moyen de le faire fonctionner sur plusieurs higherarchies ... mais je n'ai pas passé beaucoup de temps là-dessus, soit parce que je me suis coincé (dans mon projet en cours) avec acyclique.

La vraie chose cool sur le visiteur coopérative types retour. Cependant, j'utiliser mes visiteurs pour visiter des blocs entiers d'objets et faire des choses avec eux. J'ai du mal à envisager comment un retour travaillerait dans ces cas.

Le visiteur norme downcasts également fait tellement à travers le mécanisme d'appel virtuel, ce qui est plus rapide et plus sûr que parfois un casting explicite. La chose que je n'aime ce visiteur est que si vous devez visiter WidgetX dans le higherarchy Widget vous devez également mettre en œuvre la visite () fonctionnalité pour WidgetY et WidgetZ même si vous ne se soucient pas eux. Avec de grandes et / ou larges higherarchies cela peut être un PITA. D'autres options ne nécessitent pas.

Il y a aussi un « visiteur higherarchal ». Il sait quand il faut quitter.

Si vous n'êtes pas enclin à utiliser un visiteur bien et que vous souhaitez jeter juste vous pouvez envisager d'utiliser la fonction boost :: polymorphic_downcast. Il a la sécurité et les mécanismes de mise en garde fonte dynamique affirme en debug, et la vitesse de fonte statique dans un communiqué. Il peut ne pas être nécessaire si. Parfois, vous savez que vous casting droite.

La chose importante que vous devez penser et ce que vous voulez éviter, se brise le LSP. Si vous avez beaucoup de code avec "if (widget-> type () == type1) {baissés ...} else if (widget-> type () == type2) ..." puis en ajoutant de nouveaux types de widget est un gros problème qui touche beaucoup de code dans une mauvaise façon. Votre nouveau widget ne sera pas vraiment un widget parce que tous vos clients sont trop intimes avec votre higherarchy et ne savent pas. Le modèle des visiteurs ne se débarrasse pas de ce problème, mais il ne centralise, ce qui est très important quand vous avez une mauvaise odeur, et il est souvent plus simple de traiter au-dessus de celui-ci.

Il suffit de le lancer:

XItem* Item = (XItem*)Ent;

Une meilleure approche, dans l'ensemble, est la suivante:

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

Pour que cela fonctionne, vous devez activer RTTI. Regardez ici pour plus d'informations.

ont reçu une réponse, il y a 2 opérateurs:

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

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

le second est plus lent, mais plus sûr (il vérifie s'il est possible) et pourrait revenir nulle même si Ent est pas.

ai tendance à utiliser à la fois enveloppé dans une méthode:

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 cette façon, je reçois la vérification de type alors que le développement (avec une exception si quelque chose va mal) et je reçois la vitesse quand je suis fini. Vous pouvez utiliser d'autres stratégies si vous le souhaitez, mais je dois admettre que je tout à fait comme la façon suivante:)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top