Was wäre der sicherste Weg, Objekte von Klassen zu speichern, die von einer gemeinsamen Schnittstelle in einem gemeinsamen Container stammen?

7

Ich möchte eine Reihe von Objekten von Klassen verwalten, die von einer gemeinsam genutzten Schnittstellenklasse in einem gemeinsamen Container abgeleitet sind.

Um das Problem zu veranschaulichen, lasst uns sagen, ich baue ein Spiel, das verschiedene Schauspieler enthalten wird. Lassen Sie uns die Schnittstelle IActor nennen und daraus Enemy und Civilian ableiten.

Nun, die Idee ist, dass meine Hauptschleife dafür in der Lage ist:

%Vor%

und

%Vor%

... oder etwas in dieser Richtung. Dieses Beispiel wird offensichtlich nicht funktionieren, aber das ist ziemlich genau der Grund, warum ich hier frage.

Ich würde gerne wissen: Was wäre die beste, sicherste und höchste Art, diese Objekte in einem gemeinsamen heterogenen Container zu verwalten? Ich kenne eine Vielzahl von Ansätzen (Boost :: Any, void *, Handler-Klasse mit boost :: shared_ptr, Boost.Pointer Container, dynamic_cast), aber ich kann mich nicht entscheiden, welcher Weg so gehen würde.

Ich möchte auch betonen, dass ich so weit wie möglich von der manuellen Speicherverwaltung oder verschachtelten Zeigern wegbleiben möchte.

Hilfe sehr geschätzt:).

    
Svenstaro 22.03.2010, 01:24
quelle

4 Antworten

3

Wie Sie vermutet haben, müssen Sie die Objekte als Zeiger speichern.
Ich bevorzuge die Verwendung der Boost-Pointer-Container (anstelle eines normalen Containers von Smartpointern).

Der Grund dafür ist, dass der ptr-Container "boost" auf die Objekte zugreift, als wären sie Objekte (die Referenzen zurückgeben) und nicht als Zeiger. Dies erleichtert die Verwendung von Standardfunktionen und -algorithmen auf den Containern.

Der Nachteil von intelligenten Zeigern ist, dass Sie das Eigentumsrecht teilen.
Das ist nicht das, was du wirklich willst. Sie möchten, dass sich das Eigentumsrecht an einem einzigen Ort befindet (in diesem Fall dem Container).

%Vor%

und

%Vor%     
Martin York 22.03.2010, 14:27
quelle
10

Um das Problem zu lösen, das Sie erwähnt haben, obwohl Sie in die richtige Richtung gehen, aber Sie tun es in die falsche Richtung. Das müssen Sie tun

  • Definieren Sie eine Basisklasse (die Sie bereits tun) mit virtuellen Funktionen, die in Ihrem Fall von den abgeleiteten Klassen Enemy und Civilian überschrieben würden.
  • Sie müssen einen geeigneten Container auswählen, in dem Ihr Objekt gespeichert wird. Sie haben ein std::vector<IActor> genommen, was keine gute Wahl ist
    • Erstens, wenn Sie Objekte zum Vektor hinzufügen, führt dies zum Objekt-Slicing. Dies bedeutet, dass nur der IActor -Teil von Enemy oder Civilian anstelle des gesamten Objekts gespeichert wird.
    • Zweitens müssen Sie Funktionen abhängig vom Typ des Objekts aufrufen ( virtual functions ), was nur passieren kann, wenn Sie Zeiger verwenden.

Beide oben genannten Gründe weisen auf die Tatsache hin, dass Sie einen Container verwenden müssen, der Zeiger enthalten kann, etwa wie std::vector<IActor*> . Aber eine bessere Wahl wäre es, container of smart pointers zu verwenden, was Sie vor Problemen beim Speichermanagement bewahrt. Sie können einen der Smart Pointer je nach Bedarf verwenden (aber nicht auto_ptr )

So würde Ihr Code aussehen

%Vor%

und

%Vor%

Das ist Ihrem ursprünglichen Code ziemlich ähnlich, mit Ausnahme von Smart Pointer part

    
Yogesh Arora 22.03.2010 02:39
quelle
4

Meine sofortige Reaktion ist, dass Sie intelligente Zeiger im Container speichern und sicherstellen sollten, dass die Basisklasse genügend (reine) virtuelle Methoden definiert, die Sie nie wieder in die abgeleitete Klasse dynamic_cast einfügen müssen.

    
Steve314 22.03.2010 01:41
quelle
3

Wenn der Container ausschließlich die Elemente enthalten soll, verwenden Sie einen Boost-Pointer-Container: Sie sind für diesen Job konzipiert. Ansonsten benutze einen Container von shared_ptr<IActor> (und benutze sie natürlich auch richtig, was bedeutet, dass jeder, der den Besitz teilen muss, shared_ptr benutzt).

Stellen Sie in beiden Fällen sicher, dass der Destruktor von IActor virtuell ist.

void* erfordert manuelle Speicherverwaltung, das ist also nicht möglich. Boost.Any ist Overkill, wenn die Typen durch Vererbung verwandt sind - Standardpolymorphismus erledigt den Job.

Ob Sie dynamic_cast benötigen oder nicht, ist ein orthogonales Problem - wenn die Benutzer des Containers nur die IActor-Schnittstelle benötigen und Sie entweder (a) alle Funktionen der Schnittstelle virtuell machen oder (b) die Nicht-virtuelle Schnittstelle Idiom, dann brauchen Sie nicht dynamic_cast. Wenn die Benutzer des Containers wissen, dass einige der IActor-Objekte "wirklich" Zivilisten sind und Dinge nutzen wollen, die sich auf der Civilian-Oberfläche befinden, aber nicht auf IActor, dann brauchen Sie Umwandlungen (oder ein Redesign).

>     
Steve Jessop 22.03.2010 11:56
quelle