2016-07-14 14 views
1

Ich versuche, eine List-Klasse mit dem Konzept der verknüpften Listen zu machen, und während ich ursprünglich das C++ - Standard new Schlüsselwort verwendet habe, entschied ich, es für die C++ 11 std::shared_ptr zu deaktivieren . Ich kann jedoch nicht das Programm richtig funktionieren, wenn Sie intelligente Zeiger verwenden, wie es abstürzt. Hier sind einige Teile des Codes vor der Änderung:C++ Setzen Zeiger gleich shared_ptr

class List 
{ 
public: 
    void push_back(...) { 
     Node *temp = new Node; 
     ... 
     if (!head) { 
      head = temp; 
      return; 
     } 
     else { 
      Node *last = head; 
      ... 
      last->next = temp; 
     } 
    } 
    ... 

private: 
    Node *head = nullptr; 
}; 

Und hier ist, wie es mit dem Wechsel aussieht:

class List 
{ 
public: 
    void push_back(...) { 
     std::shared_ptr<Node> temp(new Node); 
     ... 
     if (!head) { 
      head = temp.get(); 
      return; 
     } 
     else { 
      Node *last = head; 
      ... 
      last->next = temp.get(); 
     } 
    } 
    ... 

private: 
    Node *head = nullptr; // don't need this to be a smart ptr 
}; 

Ich fühle mich wie das Problem sein könnte, dass head und last nicht dynamisch zugewiesen und vielleicht müssen sie mit einer shared_ptr arbeiten, aber ich bin mir nicht sicher. Was genau mache ich falsch und wie kann ich es beheben? Ich hoffe wirklich, dass dies kein Duplikat ist, weil ich nichts finden kann, was mein Problem löst. Vielen Dank.

Edit: Hier ist die Node Struktur:

struct Node{ 
    int data; 
    Node* next; 
}; 
+0

Auf den ersten Blick würde ich vermuten, das Problem ist wahrscheinlich die Definition von 'Node' – Assimilater

+0

Nun, es scheint, als ob ich den' temp' Knoten dynamisch zugewiesen werden muss oder sonst das Programm nicht funktioniert, und ich war nur wenn man bedenkt, dass man es dynamisch mit einem 'shared_ptr' statt mit einem Standard' new' teilt. Und ich kann die 'Node'-Struktur veröffentlichen, wenn das hilft. –

+0

ja, die 'Node' struct :) – Assimilater

Antwort

1

Ihr Hauptproblem ist, dass, wenn Sie shared_ptr, verwenden werden am besten, es zu benutzen den ganzen Weg. Machen Sie next eine shared_ptr statt einer rohen.

struct Node { 
    int data; 
    std::shared_ptr<Node> next; 
} 

Was std::shared_ptr unter der Haube tut, ist eine Zählung zu halten, wie viele Referenzen dort auf einen Zeiger sind. Wenn Sie Konstrukteuren Kopie verwenden oder operator= es erhöht den Referenzzähler. Wenn eine Instanz fällt aus dem Rahmen in destructor resultierenden aufgerufen wird (oder Sie geben einen unterschiedlichen Zeiger mit operator=) dekrementiert die Bezugszählung. Wenn die Zählung Null ist, wird der Zeiger zerstört.

// pass by value invokes copy constructor (refcount + 1) 
void myFunc(std::shared_ptr<MyClass> var) { 

    // Code using var 

} // end of function invokes destructor (refcount - 1) 

void run() { 
    std::shared_ptr<MyClass> ptr(new MyClass); // refcount = 1 
    myFunc(ptr); // refcount = 2 
    // After myFunc returns refcount = 1 

} 
int main() { 
    run(); // refcount = 1 
    // After run returns, refcount = 0 and the pointer is deleted 
} 

von get() Verwendung führen Sie einen Zeiger auf Speicher, der zu einem bestimmten Zeitpunkt gelöscht werden kann, unabhängig davon, ob diese Zeiger herum ist. Dies kann zu segfaults führen, wenn die rohen Zeiger auf Speicher zeigen, der shared_ptr gelöscht wurde.

Dies liegt daran, get() nicht den Referenzzähler beeinflussen. Wie könnte es sein? Es ist kein shared_ptr mehr, so dass die Klassendefinition keine Möglichkeit hat, zu wissen, was Sie damit machen oder wann es gelöscht wird. Wenn get() die Referenzzählung erhöht, würde es nichts nachher geben, und der Speicher würde niemals freigegeben werden. Das ist ein Speicherleck!

+0

Ahh, ich verstehe. Ich habe alle Vorkommen von Zeigern durch intelligente Zeigern ersetzt (nicht unbedingt dynamisch zugewiesen wie Ihre), und dass sein Problem behoben wurde. Vielen Dank. –

+0

Eigentlich durch diese Frage ausgelöst, habe ich heute gelernt, dass sie auch als eine verkettete Liste * selbst * implementiert werden können (muss die Ironie lieben). Siehe: http://stackoverflow.com/questions/725142/how-does-a-reference-counting-smart-pointers-reference-counting-work – Assimilater

+0

Hey, das ist ziemlich gut! –

4

Der Grund std::shared_ptr in erster Linie zu haben ist std::shared_ptr nehmen vollständige und uneingeschränkte Eigentum des Zeigers zu haben, und es std::shared_ptr in der Verantwortung zu delete es, wenn der letzte Verweis auf den Zeiger geht weg zu machen. Genau darum geht es bei std::shared_ptr.

Dies bedeutet, dass, sobald ein Zeiger in eine std::shared_ptr platziert wird, die std::shared_ptr nun vollständige und volle Verantwortung für die Verwaltung des Zeigers übernimmt. Es besitzt es vollständig.

Es macht daher keinen Sinn, einen Zeiger in ein std::shared_ptr zu setzen ...und dann nehmen Sie es sofort aus:

head = temp.get(); 

Es gibt Gründe für die get() Funktion zu existieren, aber dies ist nicht einer von ihnen.

Um std::shared_ptr richtig zu verwenden, muss alles ein std::shared_ptr sein. head muss ein std::shared_ptr sein:

std::shared_ptr<Node> head; // yes, it does need to be a smart ptr 

Warum braucht es ein std::shared_ptr zu sein? Nun, wenn es nicht ist, was denken Sie, wird passieren, wenn dieses:

std::shared_ptr<Node> temp(new Node); 

Insbesondere wenn diese temp intelligenten Zeiger zerstört wird, wenn diese Funktion zurückkehrt? Nun, da es die letzte std::shared_ptr sein wird, die diese Node referenziert, wird es glücklich delete es sein. Die Tatsache, dass Sie get() es früher, und legte es in head spielt keine Rolle. Jetzt haben Sie also einen head, der auf einen delete d Knoten zeigt. Heiterkeit folgt.

Und deshalb muss alles ein std::shared_ptr sein. Nicht nur head, sondern auch Node ‚s next Mitglied auch Bedürfnisse ein std::shared_ptr auch sein.

Jetzt gibt es eine Falle mit kreisförmigen Referenzen, die ins Spiel kommt, wenn std::shared_ptr das Bild betritt. Aber das wird eine andere Frage sein.

+0

Danke für die tolle, ausführliche Antwort. Ich entschied mich dafür, Assimilaters Antwort zu akzeptieren, da seine Faust kam und das Problem löste, aber ich mag deine Antwort :) –

+0

@ArchieGertsman Ich habe meine Antwort mit dieser Art von Informationen bearbeitet, als diese Antwort aufkam :). Es kann Ihnen helfen, ein wenig mehr von dem zu verstehen, was unter der Haube passiert – Assimilater