2009-09-03 3 views
5

Nur eine konzeptionelle Frage, in die ich hineingeraten bin. In meinem aktuellen Projekt fühlt es sich so an, als ob ich die Boost-Bibliotheken smart_ptr und ptr_container überbeansprucht habe. Ich habe boost::ptr_vectors in vielen verschiedenen Objekten erstellt und die Methode transfer() aufgerufen, um bestimmte Zeiger von einem boost::ptr_vector auf einen anderen zu verschieben.Sollte boost :: ptr_vector an Ort und Stelle std :: vector die ganze Zeit verwendet werden?

Es ist mein Verständnis, dass es wichtig ist, das Eigentum von Heap allokierten Objekten deutlich zu zeigen.

Meine Frage ist, wäre es wünschenswert, diese Boost-Bibliotheken verwenden, um Heap-allokierte Mitglieder, die zu einem Objekt gehören, aber dann normale Zeiger auf diese Mitglieder über get() bei der Durchführung einer Verarbeitung zu verwenden.

Zum Beispiel ... Ein Spiel könnte eine Sammlung von Tiles haben, die dazu gehören. Es könnte sinnvoll sein, diese Kacheln in einem boost::ptr_vector zu erstellen. Wenn das Spiel beendet ist, sollten diese Kacheln automatisch freigegeben werden.

Allerdings, wenn ich diese Fliesen in Beutel Objekt setzen wollen vorübergehend, sollte ich eine andere boost::ptr_vector in der Tasche erstellen und übertragen Sie die Fliesen des Spiels an die Tasche über transfer() oder sollte ich eine std::vector<Tile*>, wo die Fliesen * 's Referenz erstellen die Fliesen im Spiel und das an die Tasche weitergeben?

Danke.

** Edit Ich sollte darauf hinweisen, dass in meinem Beispiel das Spiel ein Bag-Objekt als Mitglied haben würde. Die Tasche würde nur mit Spielsteinen gefüllt werden, die das Spiel besitzt. Also würde die Tasche ohne das Spiel nicht existieren.

+2

Warum sind Ihre Objekte in erster Linie Heap-allokiert? Da sind die ersten Alarmglocken für mich. 'ptr_vector' ist eine großartige Lösung in den * seltenen * Fällen, in denen Zeiger auf Heap-allokierte Objekte gespeichert werden müssen. Aber wenn diese Fälle nicht (sehr) selten sind, dann machst du es falsch. ;) Wenn das Spiel eine Sammlung von Tiles hat, lege * sie * in den Vektor. Zeig ihnen keine Hinweise darauf. – jalf

+1

Die Antworten auf diese Frage sind ziemlich veraltet, wenn Sie eine C++ 11-Implementierung haben. Zum Beispiel gibt es keinen Grund für die Klassen ptr_x in C++ 11. Die Standardcontainer benötigen nur ihren Inhalt, um beweglich zu sein, was bedeutet, Container von ':: std :: unique_ptr's sind der Weg dafür. – Omnifarious

+0

@jalf Ich bin verwirrt. Wenn ich also einen Vektor eines komplexen Objekts (z. B. aus einer Datei) auffüllen muss, sollte ich ihn nicht immer auf dem Heap zuweisen? Wenn ich es nicht in den Heapspeicher zuteile, sollte das Objekt kopierbar sein und das gesamte Objekt in den Vektor kopiert werden. Also, wenn ich das richtig befolge, ist es normalerweise besser, die Kosten für die Kopie zu tragen, als ein shared_ptr zu benutzen, oder? Und normalerweise möchten Sie nur dann ptr_container verwenden, wenn das Objekt nicht kopierbar ist? – Jerahmeel

Antwort

15

Sie sollten nur Besitzer von Smartpointern und Pointercontainern verwenden, in denen die Übertragung des Besitzes ist. Es spielt keine Rolle, ob es sich um ein vorübergehendes Objekt handelt oder nicht - es kommt nur darauf an, ob es Eigentum ist oder nicht (und somit, ob der vorherige Eigentümer das Eigentum aufgibt).

Wenn Sie eine temporäre Vektor von Zeigern nur schaffen es auf eine andere Funktion zu übergeben, und die ursprüngliche ptr_vector verweist immer noch alle jene Objekte, gibt es keine Eigentumsübertragung, und deshalb sollten Sie schlicht vector für die temporäre Objekt verwenden - genauso wie Sie verwenden einen unformatierten Zeiger, um ein einzelnes Objekt von ptr_vector an eine Funktion zu übergeben, die einen Zeiger verwendet, sie jedoch nirgendwo speichert.

1

boost::ptr_vector dient nur dazu, die Semantik der Verwendung von Vektoren von Zeigern zu verbessern. Wenn in Ihrem Beispiel das ursprüngliche Array zerstört werden könnte, während die temporäre Menge von Vektoren verwendet wird, sollten Sie definitiv einen Vektor von shared_ptr verwenden, um zu verhindern, dass sie gelöscht werden, während sie noch verwendet werden. Wenn nicht, dann kann ein einfacher alter Vektor von Zeigern geeignet sein. Ob Sie std::vector oder boost::ptr_vector wählen, ist nicht wirklich wichtig, außer in wie schön der Code, der den Vektor verwendet, aussieht.

4

In meiner Erfahrung gibt es drei Haupteigentumsmuster, die auftauchen. Ich werde sie Baum, DAG und Graph nennen.

Am häufigsten ist ein Baum. Der Elternteil besitzt seine Kinder, die wiederum seine Kinder besitzen und so weiter. auto_ptr, scoped_ptr, bare pointer und die boost ptr_x-Klassen sind, was Sie normalerweise hier sehen. Meiner Meinung nach sollten bloße Zeiger im Allgemeinen vermieden werden, da sie überhaupt keine Besitz-Semantik vermitteln.

Die zweithäufigste ist die DAG. Dies bedeutet, dass Sie das Eigentumsrecht teilen können. Die Kinder, die ein Elternteil besitzt, können auch die Kinder anderer Kinder sein, die der Elternteil besitzt. Die TR1 und Boost shared_ptr Vorlage ist hier der Hauptakteur. Referenzzählung ist eine praktikable Strategie, wenn Sie keine Zyklen haben.

Die dritthäufigste ist die vollständige Grafik. Dies bedeutet, dass Sie Zyklen haben können. Es gibt einige Strategien, um diese Zyklen zu brechen und auf Kosten einiger möglicher Fehlerquellen zu einer DAG zurückzukehren. Diese werden im Allgemeinen durch TR1 oder boost's weak_ptr template dargestellt.

Der vollständige Graph, der nicht in eine DAG mit weak_ptr zerlegt werden kann, ist ein Problem, das in C++ nicht einfach gelöst werden kann. Die einzigen guten Handler sind Speicherbereinigungsschemata. Sie sind auch die allgemeinsten, in der Lage, die anderen beiden Schemata gut zu handhaben. Aber ihre Allgemeinheit hat ihren Preis.

Meiner Meinung nach können Sie die ptr_x-Container-Klassen oder auto_ptr nicht verwenden, es sei denn, Sie sollten wirklich Container von Objekten anstelle von Behältern mit Zeigern verwenden. shared_ptr kann überstrapaziert werden. Überlegen Sie genau, ob Sie wirklich eine DAG brauchen.

Natürlich sollten Leute Container von scope_ptrs anstelle von boost ptr_x verwenden, aber das muss auf C++ 0x warten. :-(

1

In Ihrem Spiel Fliesen Beispiel mein erster Instinkt überhaupt eine vector<Tile>, kein vector<Tile*> oder ein ptr_vector. Dann Zeiger verwenden, um die Fliesen als Sie bitte, ohne sich Gedanken über das Eigentum zu schaffen wäre, sofern die Objekte, die die Zeiger halten, überleben den Vektor der Kacheln nicht.Sie ​​haben gesagt, dass es nur zerstört wird, wenn das Spiel endet, so dass es nicht schwierig sein sollte

Natürlich kann es Gründe dafür geben, dass dies nicht möglich ist B. weil Tile eine polymorphe Basisklasse ist und die Kacheln selbst nicht alle derselben Klasse angehören, aber das ist C++, nicht Java, und nicht jedes Problem benötigt immer dynamischen Polymorphismus, aber selbst wenn Sie wirklich Zeiger brauchen, können Sie trotzdem mache Kopien dieser Zeiger ohne irgendwelche Semantik Eigentum, sofern der Umfang und die Speicherdauer der Objekte verstanden wird darauf, dass er breiter als der Umfang und Dauer der Verwendung des Zeigers:

int main() { 
    vector<Tile*> v; 
    // fill in v, perhaps with heap objects, perhaps with stack objects. 
    runGame(v); 
} 

void runGame(const vector<Tile*> &v) { 
    Tile *firsttile = v[0]; 
    vector<Tile*> eventiles; 
    eventiles.push_back(v[2]); 
    eventiles.push_back(v[4]); 
    // and so on. No need to worry about ownership, 
    // just as long as I don't keep any pointers beyond return. 
    // It's my caller's problem to track and free heap objects, if any. 
} 
0

Wenn Fliesen in der Tasche platziert sind und jemand stiehlt die Tasche Du verlierst alle Kacheln. Deshalb gehören Fliesen, obwohl temporär, aber sie gehören für kurze Zeit zur Tasche. Sie sollten das Eigentum hier übertragen, nehme ich an.

Aber die bessere Meinung wäre nicht mit Eigentum zu verwirren, weil ich nicht sehe, warum es in diesem speziellen Fall benötigt wird. Wenn hinter der Szene etwas steckt, mach weiter, wähle deine Wahl.

2

Höchstwahrscheinlich ist die Lösung, die Sie suchen sind, ist

std::vector<Tile> 

Es gibt keine Notwendigkeit für Zeiger der meisten Zeit. Der Vektor kümmert sich bereits um die Speicherverwaltung der enthaltenen Objekte. Die Fliesen gehören dem Spiel, oder? Der Weg, dies explizit zu machen, besteht darin, die Objekte selbst in die Spielklasse zu setzen - die einzigen Fälle, in denen Sie normalerweise Zeiger und dynamisch zugewiesene individuelle Objekte benötigen, sind 1) Polymorphismus oder 2) gemeinsame Eigentümerschaft.

Aber Zeiger sollten die Ausnahme sein, nicht die Regel.

1

Wenn das Spiel die Plättchen besitzt, ist das Spiel für diese Aufgabe verantwortlich.

Es hört sich so an, als ob die Tasche die Objekte nie wirklich besitzt, daher sollte sie nicht für das Löschen verantwortlich sein. Also würde ich ptr_vector innerhalb des Game-Objekts verwenden. Aber verwende einen std :: vector in der Tasche.

Hinweis: Ich würde niemals zulassen, dass jemand, der die Tasche benutzt, einen Zeiger auf eine Kachel aus der Tasche holt. Sie sollten nur in der Lage sein, einen Bezug zu der Fliese aus der Tasche zu erhalten.

0

Ich stimme zu, dass die Verwendung von Vektor statt springen direkt in Heap-zugeteilten T ist ein guter Instinkt, aber ich kann leicht Tile eine Art sein, während Kopie Konstruktion teuer ist, die Vektorwachstumsstrategie praktisch sein könnte auszuschließen. Natürlich könnte das am besten mit einer Liste, nicht einem Vektor gelöst werden ...

Verwandte Themen