2016-10-19 74 views
-4

我做了一個使用list-node的堆棧類。 但一個正常工作,另一個總是崩潰。 兩個程序有什麼區別? 我thimk析構函數銷燬兩個程序之間的區別?

#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(); 
} 

上述方案老是死機..

#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(); 
} 

這個程序能正常工作。

兩者有什麼區別?

+1

區別在於一行在上面的代碼中被註釋掉了。上述代碼中的'free()'很可能導致「雙重釋放或損壞」錯誤。解決方法:使用智能指針(--you標記爲C++ 11)。 – davidhigh

+0

在評論之前閱讀代碼.. – Parker

回答

1

當你刪除poptmp,您撥打的Stack析構函數,這在你第一個代碼雙打free

這是你的棧的狀態推1和2(與p1p2是新的後在push創建Stack對象):

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

現在,如果你在你的第一個代碼破壞s像(忽略會發生什麼):

  • s.free被執行。
  • s.nextp1,所以執行s.pop
  • tmp變成s.next,即p1
  • s.next變成s.next->next,即p1->next,即p2
  • tmp,即p1,被刪除,所以調用p1的析構函數。在這一點上,我們是在以下狀態:

    | data  |  next 
    ------------------------------- 
    s | uninitialized |  p2 
    p1 |  1  |  p2 
    p2 |  2  | nullptr 
    
  • p1的析構函數調用free,這就要求pop(因爲p1->nextp2)。

  • p2通過tmp刪除。
  • 返回與下列情形s析:

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

在你的第二個代碼,p1的析構函數呼叫free,並且不會使p2懸空指針,所以代碼「正常工作」。

你應該修改pop這樣的:

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

順便說一句,如果你使用C++ 11,你應該管理的指針重新設計你的代碼。類似的東西:

#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); 
} 

隨着make_unique<Stack>()是C++ 14,你可以用unique_ptr<Stack>(new Stack)取代它。

+0

我忘了刪除也會調用析構函數。 – Parker

+0

我忘了刪除也會調用析構函數。非常感謝。 – Parker

1

在第一個,你pop被打破 - delete tmp;遞歸刪除與tmp,不只是頂級節點開始的整個亞組。
tmp的析構函數也將爲free();,依此類推)。
這會導致雙重刪除。

您的第二個版本泄漏內存,除非您在完成堆棧時記得致電free()

你可以保持析構簡單,解決這兩個問題:

virtual ~Stack() { 
    delete next; 
} 

free可以

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

(有沒有需要做大量的指針,交換和元素複製如果你會把結果扔掉。)