2016-10-19 5 views
-4

Ich habe eine Stapelklasse mit List-Node gemacht. Aber einer funktioniert gut, der andere stürzt immer ab. Was ist der Unterschied zwischen zwei Programmen? I thimk destructorUnterschied zwischen zwei Programmen?

#include<iostream> 
using namespace std; 

class Stack 
{ 
public: 
    int pop() { 
     data = next->data; 
     auto tmp = next; 
     next = next->next; 
     delete tmp; 
     return data; 
    } 
    void push(int n) { 
     Stack* p = new Stack(); 
     p->data = n; 
     p->next = next; 
     next = p; 
    } 
    virtual ~Stack() { 
     free(); 
    } 
    void free() { 
     while(next) pop(); 
    } 

protected: 
    int data; 
    Stack* next = nullptr; 
}; 
int main() 
{ 
    Stack s; 
    s.push(1); 
    s.push(2); 
    //s.free(); 
} 

oben Programm zerstört immer abstürzt ..

#include<iostream> 
using namespace std; 

class Stack 
{ 
public: 
    int pop() { 
     data = next->data; 
     auto tmp = next; 
     next = next->next; 
     delete tmp; 
     return data; 
    } 
    void push(int n) { 
     Stack* p = new Stack(); 
     p->data = n; 
     p->next = next; 
     next = p; 
    } 
    virtual ~Stack() { 
    // free(); 
    } 
    void free() { 
     while(next) pop(); 
    } 

protected: 
    int data; 
    Stack* next = nullptr; 
}; 
int main() 
{ 
    Stack s; 
    s.push(1); 
    s.push(2); 
    s.free(); 
} 

dieses Programm funktioniert gut.

Was ist der Unterschied zwischen den beiden?

+1

Der Unterschied ist, dass eine Zeile im oberen Code auskommentiert ist. Es ist sehr wahrscheinlich, dass das 'free()' im obigen Code zu einem "double free or corruption" -Fehler führt. Problemumgehung: Verwenden Sie einen Smart-Zeiger (- Sie haben C++ 11 getaggt). – davidhigh

+0

lesen Sie den Code, bevor Sie kommentieren. – Parker

Antwort

1

Wenn Sie tmp in pop löschen, rufen Sie Stack ‚s destructor, die in Ihnen ersten Code free verdoppelt:

Hier ist der Zustand des Stapels nach dem Drücken 1 und 2 (mit p1 und p2 die neuen Wesen Stack Objekte in push erstellt):

| data  |  next 
------------------------------- 
s | uninitialized |  p1 
p1 |  1  |  p2 
p2 |  2  | nullptr 

Nun, wenn Sie s wie in Ihrem ersten Code zerstören (zu ignorieren, was fürgeschieht):

  • s.free wird ausgeführt.
  • s.next ist p1, also wird s.pop ausgeführt.
  • tmp wird s.next, was p1 ist.
  • s.next wird s.next->next, das ist p1->next, das ist p2.
  • tmp, die p1 ist, wird gelöscht, so p1 Destruktor aufgerufen wird. An diesem Punkt sind wir in dem folgenden Zustand:

    | data  |  next 
    ------------------------------- 
    s | uninitialized |  p2 
    p1 |  1  |  p2 
    p2 |  2  | nullptr 
    
  • p1 ‚s destructor nennt free, die nennt pop (weil p1->next ist p2).

  • p2 wird durch tmp gelöscht.
  • Zurück zur destructor von s mit dem folgenden Zustand:

    | data  |  next 
    ------------------------------- 
    s | uninitialized |  p2 (dangling) 
    p2 |  2  | nullptr 
    

In Ihrem zweiten Code, p1 ‚s destructor nicht Anruf free, und macht nicht p2 einen baumelnden Zeiger, so der Code "funktioniert gut".

Sie pop wie dies ändern sollte:

int pop() { 
    data = next->data; 
    auto tmp = next; 
    next = next->next; 
    /***********************/ 
    next->next = nullptr; 
    /***********************/ 
    delete tmp; 
    return data; 
} 

By the way, wenn Sie C++ 11 verwenden, sollten Sie Ihren Code mit einem verwalteten Zeiger neu zu gestalten. So etwas Ähnliches:

#include<iostream> 
#include<memory> 
using namespace std; 

class Stack 
{ 
public: 
    int pop() { 
     data = next->data; 
     next = move(next->next); 
     return data; 
    } 
    void push(int n) { 
     auto p = make_unique<Stack>(); 
     p->data = n; 
     p->next = move(next); 
     next = move(p); 
    } 
    void free() { 
     while(next) pop(); 
    } 

protected: 
    int data; 
    unique_ptr<Stack> next; 
}; 
int main() 
{ 
    Stack s; 
    s.push(1); 
    s.push(2); 
} 

Mit make_unique<Stack>() C++ 14 zu sein, können Sie es mit unique_ptr<Stack>(new Stack) ersetzen.

+0

Ich habe vergessen, dass Löschen auch den Destruktor aufrufen wird. – Parker

+0

Ich habe vergessen, dass Löschen auch den Destruktor aufrufen wird. Danke vielmals. – Parker

1

In der ersten, pop ist defekt - delete tmp; löscht rekursiv den gesamten Unterstapel, der mit tmp beginnt, nicht nur der oberste Knoten.
(Der Destruktor von tmp wird auch free();, und so weiter.)
Dies führt zu doppelten Löschungen.

Ihre zweite Version verliert Speicher, es sei denn, Sie erinnern sich, free() zu rufen, wenn Sie mit dem Stapel fertig sind.

Sie den destructor einfach halten und beiden Probleme beheben:

virtual ~Stack() { 
    delete next; 
} 

und free können

void free() 
{ 
    delete next; 
    next = nullptr; 
} 

sein (Es gibt keine Notwendigkeit, eine Menge Zeiger-Swapping und Element-Kopieren zu tun, wenn Sie werden das Ergebnis wegwerfen.)

Verwandte Themen