Frage

Ich arbeite an einem kleinen roguelike Spiel, und für jedes Objekt / „Ding“, das kein Teil der Karte ist, aus einer XEntity Klasse basiert. Es gibt mehrere Klassen, die davon abhängen, wie XPlayer, XItem und XMonster.

Mein Problem ist, dass ich einen Zeiger von XEntity zu XItem konvertieren will, wenn ich weiß, dass ein Objekt in Artikel ist. Der Beispielcode verwende ich einen Artikel zu holen ist dies, ist es, wenn eine andere Person aufnimmt ein Element ist es steht über.

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

Mein erster Gedanke war, ein Verfahren in XEntity zu schaffen, dass die Renditen sich als XItem Zeiger, aber es zirkuläre Abhängigkeiten schafft, die unauflösbar sind.

Ich bin mir ziemlich über dieses stumped. Jede Hilfe wird sehr geschätzt.

War es hilfreich?

Lösung

Wenn Sie weiß , dass die XEntity actuall und XItem ist, dann können Sie einen statischen Guss verwenden.

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

Sie sollten jedoch Sie entwerfen und sehen, wenn Sie auf der Einheit in einer Art und Weise betrieben werden kann, dass bedeutet, dass Sie brauchen nicht zu wissen, welche Art abgeleitet ist. Wenn Sie die Basisklasse eine ausreichend reich Schnittstelle geben können Sie die Flagprüfung Typ Inspektion beseitigen kann können.

Andere Tipps

Casting löst das Problem, wie andere haben darauf hingewiesen:

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

Allerdings denke ich, dass Sie sould zurück und Blick auf der Gestaltung des Programms fort, als Downcasting etwas ist, das soll und kann durch ein geeignetes objektorientiertes Design vermieden werden. Ein leistungsstarkes, obwohl ein wenig komplex, Werkzeug könnte die Besuchermuster .

Ich habe das Downcasting zu glauben, war immer möglich, mit einem „richtigen“ Design zu vermeiden. Das ist einfach nicht der Fall though. Eine richtige Design muss sehr oft Unterobjekte haben, die neu zu implementieren, und nicht nur ein anderes Verhalten. Allzu oft spricht von „richtigen“ Design werden Ihnen sagen, das neue Verhalten auf den Stapel der Abstraktion an Orten zu gehen, wo es nicht hingehört. Nicht immer, aber wenn Sie immer wieder versuchen, sicherzustellen, dass alle Ihre Klassen machen von dem abstrakteste Punkt verwendet werden, dies ist sehr oft, wo die Dinge am Ende gehen und es ist nur fugly.

Eine große Möglichkeit, mit einziehe in einer zentralisierten Weise zu behandeln ist durch das Besuchermuster verwenden. obwohl Es gibt mehrere Formen der Besucher, erfordern einziehe einige, manche nicht. Der azyklische Besucher, die eine, die einziehenden erfordern, ist einfacher zu arbeiten mit und ist nach meiner Erfahrung, mächtiger.

Ein anderer Besucher, dass ich nicht mit den Ansprüchen an die Arbeit versucht haben, die gleiche Flexibilität der azyklische Besucher mit der Geschwindigkeit des Standard Besucher gerecht zu werden; es heißt „kooperativer Besucher“ genannt. Es wirft immer noch, es tut nur so in eine schnellere Weise mit ihm eigenen Lookup-Tabelle ist. Der Grund, warum ich nicht die kooperative Besucher versucht haben, ist, dass ich nicht einen Weg gefunden, um es auf mehreren higherarchies funktioniert ... aber ich habe nicht viel Zeit damit verbracht, entweder weil ich mich gesteckt haben (in meinem aktuelles Projekt) mit azyklisch.

Die wirkliche coole Sache, über die kooperativen Besucher sind Rückgabetypen. Allerdings benutze ich meine Besucher ganze Blöcke von Objekten zu besuchen und Dinge zu tun, mit ihnen. Ich habe Probleme Absehen, wie eine Rückkehr würde in diesen Fällen arbeiten.

Der Standard-Besucher downcasts tut es auch nur so durch den virtuellen Call-Mechanismus, der schneller ist und manchmal sicherer als eine explizite Umwandlung. Die Sache, die ich nicht mag diesen Besucher ist, dass, wenn Sie WidgetX im Widget higherarchy müssen besuchen, dann müssen Sie auch Besuch () Funktionalität für WidgetY und WidgetZ selbst implementieren, obwohl Sie nicht über sie kümmern. Mit großem und / oder breiten higherarchies kann dies ein PITA sein. Andere Optionen nicht, dies erfordern.

Es gibt auch einen „higherarchal Besucher“. Sie weiß, wann sie beenden.

Wenn Sie nicht geneigt einen Besucher zu verwenden, obwohl und Wunsch gerade gegossenen dann könnten Sie die boost :: polymorphic_downcast Funktion. Es hat die Sicherheits- und Warnmechanismen dynamischer Umwandlung mit behauptet in Debug-Builds, und die Geschwindigkeit des statischen Gusses in einer Pressemitteilung. Es kann nicht notwendig, obwohl sein. Manchmal weiß man nur, dass du recht hast Gießen.

Das Wichtigste ist, dass Sie darüber nachdenken müssen, und was wollen Sie vermeiden, bricht die LSP. Wenn Sie eine ganze Menge Code mit haben "if (Widget-> Typ () == Typ1) {gesenkten ...} else if (Widget-> Typ () == Typ2) ...", dann das Hinzufügen neuer Widget-Typen ist ein großes Problem, das eine Menge Code in einem schlechten Weg beeinflusst. Ihr neues Widget wird nicht wirklich ein Widget sein, da alle Ihre Kunden zu intim mit Ihrem higherarchy und wissen nicht darüber. Das Besuchermuster dieses Problem nicht los bekommt, aber es tut zentralisieren, was sehr wichtig ist, wenn Sie einen schlechten Geruch haben, und es macht oft einfacher, sie mit auf ihm zu beschäftigen.

Just warf es:

XItem* Item = (XItem*)Ent;

Ein besserer Ansatz, insgesamt ist dies:

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

Damit das funktioniert, müssen Sie RTTI ermöglichen. Schauen Sie hier für weitere Informationen.

Wie beantwortet wurde, gibt es 2 Operatoren:

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

Und:

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

Die zweite ist langsamer, aber sicherer (es wird geprüft, ob es möglich ist) und vielleicht null zurück, auch wenn Ent nicht.

Ich neige dazu, sowohl in einem Verfahren eingewickelt zu verwenden:

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
}

So kann ich die Typüberprüfung während der Entwicklung erhalten (mit einer Ausnahme, wenn etwas schlecht geht) und ich bekomme Geschwindigkeit, wenn ich fertig bin. Sie können andere Strategien verwenden, wenn Sie wollen, aber ich muß mich ganz wie auf diese Weise zugeben:)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top