Was wäre der sicherste Weg zum Speichern von Objekten der Klassen von einer gemeinsamen Schnittstelle in einem gemeinsamen Behälter abgeleitet werden?

StackOverflow https://stackoverflow.com/questions/2489533

Frage

Ich möchte eine Reihe von Objekten der Klassen aus einer gemeinsamen Interface-Klasse in einem gemeinsamen Behälter abgeleitet verwalten.

Um das Problem zu veranschaulichen, lassen Sie uns sagen, ich bin ein Spiel bauen die verschiedenen Akteure enthalten wird. Nennen wir die Schnittstelle IActor und derive Enemy und Civilian von ihm.

Nun ist die Idee, mein Spiel Hauptschleife der Lage sein, dies zu tun:

// somewhere during init
std::vector<IActor> ActorList;
Enemy EvilGuy; 
Civilian CoolGuy;
ActorList.push_back(EvilGuy);
ActorList.push_back(CoolGuy);

und

// main loop
while(!done) {
    BOOST_FOREACH(IActor CurrentActor, ActorList) {
        CurrentActor.Update();
        CurrentActor.Draw();
    }
}

... oder etwas in diese Richtung. Dieses Beispiel wird offensichtlich nicht arbeiten, aber das ist so ziemlich der Grund, warum ich hier bin gefragt.

Ich möchte wissen: Was wäre die beste, sicherste, auf höchster Ebene Weg, um die Objekte in einem gemeinsamen heterogenen Behälter zu verwalten? Ich weiß, über eine Vielzahl von Ansätzen (boost :: any, void *, Handler-Klasse mit boost :: shared_ptr, Boost.Pointer Container, dynamic_cast), aber ich kann nicht entscheiden, welches der richtige Weg sein würde, hier zu gehen.

Auch würde Ich mag betonen, dass ich weg, so weit wie möglich bleiben will von den manuellen Speicherverwaltung oder verschachtelten Zeigern.

Hilfe sehr geschätzt.)

War es hilfreich?

Lösung

Wie Sie vielleicht erraten haben, müssen Sie die Objekte als Zeiger speichern.
Ich ziehe die Boost-Zeiger-Container (statt einen normalen Container von Smart Pointer) zu verwenden.

Der Grund hierfür ist der Boost-ptr Container Zugriff auf die Objekte, als ob sie Objekte waren (Rückkehr Referenzen) als Zeiger. Dies macht es einfacher Standard functors und Algorithmen auf die Behälter zu verwenden.

Der Nachteil von Smart-Pointer ist, dass Sie Eigentum teilen.
Das ist nicht das, was Sie wirklich wollen. Sie wollen das Eigentum an einem einzigen Ort zu sein (in diesem Fall den Behälter).

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian());

und

std::for_each(ActorList.begin(), 
              ActorList.end(),
              std::mem_fun_ref(&IActor::updateDraw));

Andere Tipps

Um das Problem zu lösen, die Sie erwähnt haben, auch wenn Sie in die richtige Richtung gehen, aber Sie sind es die falsche Art und Weise zu tun. Dies ist, was würden Sie tun müssen, um

  • Definieren Sie eine Basisklasse mit virtuellen Funktionen (die Sie bereits tun), die von abgeleiteten Klassen Enemy und Civilian in Ihrem Fall außer Kraft gesetzt würde.
  • Sie benötigen einen geeigneten Behälter wählen mit speichert Ihr Objekt. Sie haben einen std::vector<IActor> genommen, die nicht eine gute Wahl ist, weil
    • Erstens, wenn Sie Objekte auf den Vektor hinzufügen, um es zu Objekt Schneiden führt. Dies bedeutet, dass nur die IActor Teil Enemy oder Civilian wird anstelle des gesamten Objekts gespeichert.
    • Zweitens müssen Sie Funktionen aufrufen, je nach Art des Objekts (virtual functions), die nur, wenn Sie Zeiger verwenden passieren kann.

Sowohl der Grund oben weisen darauf hin, dass Sie benötigen, um einen Behälter zu verwenden, die Zeiger, so etwas wie std::vector<IActor*> enthalten. Aber eine bessere Wahl zu verwenden container of smart pointers wäre, die Sie aus dem Speicher-Management-Kopfschmerzen sparen. Sie können eine des Smart-Pointer verwenden, je nach Ihrem Bedarf (aber nicht auto_ptr)

Dies ist, was Ihr Code würde wie folgt aussehen

// somewhere during init
std::vector<some_smart_ptr<IActor> > ActorList;
ActorList.push_back(some_smart_ptr(new Enemy()));
ActorList.push_back(some_smart_ptr(new Civilian()));

und

// main loop
while(!done) 
{
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    {
        CurrentActor->Update();
        CurrentActor->Draw();
     }
}

Welche ziemlich ähnlich zu Ihrem ursprünglichen Code ist mit Ausnahme von Smart Pointer Teil

Meine sofortige Reaktion ist, dass Sie intelligente Zeiger in dem Behälter gespeichert werden sollen, und stellen Sie sicher, dass die Basisklasse definiert genug (rein) virtuelle Methoden, dass man nie auf die abgeleitete Klasse dynamic_cast zurück müssen.

Wenn Sie die Container wollen ausschließlich die Elemente darin besitzen, einen Boost-Zeiger-Container verwenden: sie sind für diesen Job entwickelt. Ansonsten einen Behälter mit shared_ptr<IActor> verwenden (und natürlich sie richtig nutzen, dass jeder Sinn, die auf den Aktienbesitz Nutzungen shared_ptr muss).

In beiden Fällen stellen Sie sicher, dass der Destruktor von IActor virtuell ist.

void* erfordert, dass Sie manuelle Speicherverwaltung zu tun, das ist also aus. Boost.Any ist übertrieben, wenn die Arten von Vererbung verwandt sind -. Standard-Polymorphismus den Job

Ob Sie dynamic_cast brauchst oder nicht, ist ein orthogonales Problem - wenn die Benutzer des Behälters nur die IActor Schnittstelle benötigen, und Sie entweder (a) machen alle die Funktionen der Schnittstelle virtuelle oder auch (b) die nicht virtuelle Schnittstelle Idiom, dann brauchen Sie nicht dynamic_cast. Wenn die Benutzer des Behälters wissen, dass einige der IActor Objekte sind „wirklich“ Zivilisten und wollen Gebrauch der Dinge machen, die in der zivilen Schnittstelle sind aber nicht IActor, dann werden Sie brauchen Abgüsse (oder ein Redesign).

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