2010-09-25 22 views
8

Ich wurde vor kurzem in die Existenz von auto_ptr und shared_ptr eingeführt und ich habe eine ziemlich einfache/naive Frage.Zeiger vs auto_ptr vs shared_ptr

Ich versuche, eine Datenstruktur zu implementieren, und ich muss auf die Kinder eines Node zeigen, die (mehr als 1 und seine) Zahl ändern können. Welches ist die beste Alternative und warum:

class Node 
{ 
    public: 
     // ... 
     Node *children; 

    private: 
     //... 
} 

class Node 
{ 
    public: 
     // ... 
     share_ptr<Node> children; 

    private: 
     //... 
} 

Ich bin nicht sicher, aber ich denke, auto_ptr nicht für Arrays funktioniert. Ich bin mir auch nicht sicher, ob ich Doppelzeiger benutzen soll. Danke für jede Hilfe.

+0

auto_ptr ist veraltet in C++ 11 und sollte auch möglichst in Code in älteren C++ Version vermieden werden. – Nikko

+0

'auto_ptr' ist veraltet, weil es unnötig schwierig ist, es richtig zu verwenden. Verwenden Sie stattdessen 'unique_ptr', was im Grunde genommen dasselbe wie' auto_ptr' ist, nur dass es korrekt funktioniert und auch Arrays unterstützt. Es ist seit C++ 11 verfügbar. – nwp

+0

siehe auch http://stackoverflow.com/questions/3987521/how-bad-is-to-use-void-pointer-in-stdvector-declaration – fizzbuzz

Antwort

7

Sie haben Recht, dass auto_ptr nicht für Arrays funktioniert. Wenn es das Objekt zerstört, das es besitzt, verwendet es delete object;, wenn Sie also new objects[whatever]; verwenden, erhalten Sie undefiniertes Verhalten. Vielleicht etwas subtiler, auto_ptr entspricht nicht den Anforderungen von "Copyable" (wie der Standard den Begriff definiert), so dass Sie auch keinen Container (Vektor, Deque, Liste, etc.) von auto_ptr erstellen können.

Ein shared_ptr ist auch für ein einzelnes Objekt. Es ist für eine Situation, in der Sie das Eigentumsrecht geteilt haben und das Objekt nur löschen müssen, wenn alle die Eigentümer aus dem Bereich gehen. Wenn nicht etwas vor sich geht, von dem Sie uns nichts erzählt haben, sind die Chancen ziemlich gut, dass es auch nicht Ihren Anforderungen entspricht.

Vielleicht möchten Sie sich noch eine andere Klasse ansehen, die Ihnen vielleicht noch neu ist: Boost ptr_vector. Zumindest basierend auf dem, was Sie gesagt haben, scheint es Ihre Anforderungen besser als entweder auto_ptr oder shared_ptr würde passen.

+0

Danke! Also bekomme ich, dass ich mit 'ptr_vector' oder' Node * children' gehen soll. Kann ich 'auto_ptr' nicht verwenden, um auf einen' std :: vector' von 'Node's zu zeigen? Ist das 'Node * children' richtig oder ich bevorzuge' Node ** children'? Ich bin etwas verwirrt. Entschuldigung für das Packen zu viele Fragen hier. –

+0

@myle: Ohne mehr darüber zu wissen, was Sie tun, ist es schwer zu sagen. Im Grunde wird ein 'Knoten *' Ihnen einen Punkt zu einer beliebigen Anzahl von Knoten geben, so dass diese Knoten im Grunde Teil ihres Elternteils sind und nicht nur damit verbunden sind. Ein 'Knoten **' wird Ihnen ein dynamisches Array von Zeigern zu Knoten geben, aber Sie müssen das dynamische Array selbst verwalten. –

3

Ich habe erfolgreich in einer ähnlichen Situation verwendet.

Der Hauptvorteil der Verwendung eines Vektors von shared_ptrs anstelle eines Arrays besteht darin, dass die gesamte Ressourcenverwaltung für Sie erledigt wird. Dies ist besonders nützlich in zwei Situationen:
1) Wenn der Vektor nicht mehr im Geltungsbereich ist, ruft er automatisch alle Inhalte auf. In diesem Fall wird die Referenzzahl des untergeordneten Knotens um 1 verringert, und wenn nichts anderes darauf verweist, wird delete für das Objekt aufgerufen.
2) Wenn Sie den Knoten an anderer Stelle referenzieren, besteht kein Risiko, dass ein ungeordneter Zeiger auf ein gelöschtes Objekt verbleibt. Das Objekt wird nur gelöscht, wenn es keine Referenzen mehr gibt.

Wenn Sie kein wesentlich komplizierteres Verhalten wünschen (vielleicht gibt es einen Grund, warum ein Array erforderlich ist), würde ich vorschlagen, dass dies ein guter Ansatz für Sie sein könnte.

Eine einfache Umsetzung der Idee:

class Node { 
private: 
    T contents; 
    std::vector<std::shared_ptr<Node> > children; 

public: 
    Node(T value) : contents(value) {}; 

    void add_child(T value) { 
     auto p = std::make_shared<Node>(value); 
     children.push_back(p); 
    } 

    std::shared_ptr<Node> get_child(size_t index) { 
     // Returning a shared pointer ensures the node isn't deleted 
     // while it is still in use. 
     return children.at(index); 
    } 

    void remove_child(size_t index) { 
     // The whole branch will be destroyed automatically. 
     // If part of the tree is still needed (eg. for undo), the 
     // shared pointer will ensure it is not destroyed. 
     children.erase(children.begin() + index); 
    } 

}; 
+0

Beachten Sie außerdem, dass Sie einen eigenen Destruktor für einen shared_ptr bereitstellen können, damit Sie ihn zum Verwalten eines Arrays verwenden können, indem Sie delete [] statt clear delete aufrufen. – QuesterZen

+0

Die einfachste (aber etwas hässliche) Möglichkeit, einen benutzerdefinierten Deleter für Arrays hinzuzufügen, ist die Verwendung einer Lambda-Funktion, die als zusätzliches Argument in den Konstruktor übernommen wird. ZB: 'std :: shared_ptr sp (neu T [n], [] (T * p) {löschen [] p;})'. Für unique_ptrs gibt es dafür eine spezielle Version: 'std :: unique_ptr up (new T [n])', die delete [] anstelle von delete aufruft. – QuesterZen

1

auto_ptr für std::unique_ptr und btw ist veraltet. std::unique_ptr funktioniert für Arrays. Sie brauchen nur C++ 11 Unterstützung. Und es gibt bereits viele Ressourcen zu intelligenten Zeigern und zur Verschiebung der Semantik. Der wesentliche Unterschied zwischen auto_ptr und unique_ptr ist, dass auto_ptr hat eine bewegen, wenn Sie die Kopie Konstruktor aufrufen und unique_ptr verbietet den Kopierkonstruktor, sondern ermöglicht eine move, wenn der Umzug Konstruktor aufrufen. Daher benötigen Sie C++ 11 Unterstützung mit Bewegungssemantik.

1

Stroustrup diskutiert die Frage "Was ist ein Auto_ptr und warum gibt es kein Auto_Array" und kommt zu dem Schluss, dass Letzteres nicht notwendig ist, da die gewünschte Funktionalität mit einem Vektor erreicht werden kann.

http://www.stroustrup.com/bs_faq2.html#auto_ptr