2015-11-08 183 views
5

我是一個SWE學習C++,並使用std ::的unique_ptr爲headnext引用建立一個簡單的LinkedList類。這是基本結構:C++ smart_ptr不會導致堆棧溢出?

template <class T> 
struct LinkedListNode { 
    T value; 
    std::unique_ptr<LinkedListNode<T>> next; 

    // For debugging. 
    ~LinkedListNode() { 
     std::cout << "destructed for value " << this->value << std::endl; 
    } 
}; 

template <class T> 
struct LinkedList { 
    std::unique_ptr<LinkedListNode<T>> head; 
}; 

使用智能指針,我想到的是,當一個LinkedList實例被刪除或超出範圍,則head將被刪除,並且每個next節點將遞歸也被刪除。

而這正是發生了什麼。但是,當處理真正的長列表(~20M節點)時,它仍然可以正常工作。不應該因堆棧溢出而崩潰嗎?

爲了非常粗略估計我的操作系統堆棧的大小,我寫了下面的腳本:

int main() { 
    struct s { 
     static void p(int i) { 
     std::cout << i << std::endl; 
     p(i+1); 
    }; 
    s::p(0); 
} 

而且它的迭代次數墜毀〜175K,比我能到20M節點要少得多之前解除分配。到底是怎麼回事?我是否錯過了unique_ptr的工作方式?

+0

這看起來不像'std :: unique_ptr'的智能用法。持有'unique_ptr'意味着擁有對象的所有權。在鏈接列表中,節點不擁有下一個節點的所有權,它擁有本身包含的數據的所有權。 – Jack

+0

您可以發佈您正在討論的整個代碼,以便其他人可以編譯並查看會發生什麼嗎? – Ruslan

+0

當然! https://gist.github.com/manuelmenzella/0fd85280d5051abec2c7 不要太苛刻;) –

回答

2

在你的榜樣,你有確實遞歸,背後的真正原因是沒有達到堆棧溢出可能是因爲它是一個tail-call recursion可能進行優化,以迭代求解。

有了這個代碼片段:

struct Node 
{ 
    int value; 
    std::unique_ptr<Node> next; 

    Node(int value, Node* next) : value(value), next(next) { } 

    ~Node() 
    { 
    if (value == 0) 
     cout << "foo" << endl; 
    } 
}; 

int main() 
{ 
    Node* node = new Node(0, nullptr); 
    for (int i = 1; i <= 5; ++i) 
    node = new Node(i, node); 

    delete node; 

    return 0; 
} 

通過放置在cout聲明斷點和檢查堆棧跟蹤你清楚地看到這種行爲是遞歸的:

enter image description here

的行爲通過使用基本析構函數來跟蹤~Node()何時返回也顯示here

由於next節點在從析構函數返回之前必須銷燬,這導致再次調用~Node()。這種行爲將是相同的,通過使用原始指針並直接在析構函數中刪除下一個指針,這實際上已經回答了here