2014-09-24 68 views
0

有一個類似的標題已經是幾個問題,但我一直無法找到一個SEG-故障時,析構函數調用。我一直在編寫一個模板雙向鏈表(爲了好玩和練習),到目前爲止,它一直在正常工作(測試插入和刪除所有的地方,測試邊緣案例等)。添加析構函數邏輯導致分段錯誤

我沒有正確地實現RAII到這一點,因此是非常不安全的現在實際使用(副本將是一個問題,除非你先清除列表,它會泄漏內存就像一個水桶上翹如果它超出範圍)。

我原本有一個空的析構函數:

template<class T> 
dll<T>::~dll() 
{} 

查找功能:

template<class T> 
int dll<T>::find_node(T data) 
{ 
    int index = 0; 
    dllnode<T>* current_node = end[0]; 
    while (current_node) 
    { 
    if (current_node->data == data) 
     return index; 
    current_node = current_node->link[1]; 
    index++; 
    } 
    return -1; 
} 

和工作del_node功能。現在

,我遇到的問題是,當我嘗試以清除在析構函數的任何數據的列表:

template<class T> 
dll<T>::~dll() 
{ 
    while (del_node(0)){} //returns 0 if no nodes left to delete. 
} 

我(不變)find_node()函數導致賽格故障。

由於只有更改爲代碼是在析構函數,我'假設'它是相關的,但我不知道如何。應該永遠不要調用析構函數,因爲dll對象位於堆棧上,並且在範圍內直到main結束。

使用空的析構函數,代碼按預期運行。

這裏是當前示例代碼:

dll_example.cpp:

#include <iostream> 
#include <climits> 
#include "dll.h" 

void printout(dll<int> list) 
{ 
    std::cout<<"\nnumber of nodes: "<<list.get_node_count()<<std::endl; 
    std::cout<<"list contents:"<<std::endl; 
    list.print_list(); 
} 

int main() 
{ 
    dll<int> list; 

    std::cout<<"created list" << std::endl; 

    for (int i = 0; i<10; i++) 
    { 
     int j = list.add_node(i, 5); 
     std::cout<<"added a node, value: "<<i<<", index: "<< j << std::endl; 
    } 

    printout(list); 

    std::cout<<"\nfinding '8'" << std::endl;     //Prints this line 
    int index = list.find_node(8);       
    std::cout<<"deleting '8' (index: "<<index<<")"<< std::endl; //never gets this far 
    list.del_node(index); 

    printout(list); 

    std::cout<<"\ndeleting #1" << std::endl; 
    list.del_node(1); 

    printout(list); 

    std::cout<<"\ndeleting #0" << std::endl; 

    do{          //manually delete all entries including 
              //a del_node() call on empty list (works) 
     printout(list); 
     std::cout<<"deleting #0" << std::endl; 
    }while(list.del_node(0)); 
    printout(list); 
} 

故障輸出:

$ ./dll_example.exe 
created list 
added a node, value: 0, index: 0 
added a node, value: 1, index: 1 
added a node, value: 2, index: 2 
added a node, value: 3, index: 3 
added a node, value: 4, index: 4 
added a node, value: 5, index: 5 
added a node, value: 6, index: 5 
added a node, value: 7, index: 5 
added a node, value: 8, index: 5 
added a node, value: 9, index: 5 

number of nodes: 10 
list contents: 
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 

finding '8' 
Segmentation fault (core dumped) 

空析構函數輸出:

... 
list contents: 
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 

finding '8' 
deleting '8' (index: 6) 

number of nodes: 9 
list contents: 
0, 1, 2, 3, 4, 9, 7, 6, 5, 
... 
+0

_「和一個工作del_node」函數_它實際上做了什麼? – 2014-09-24 15:27:28

+1

您正將一個列表按值傳遞給'printout'。這使用你的拷貝構造函數*和*你的析構函數。三條規則不會原諒,三條規則不會忘記(並且在你看另一條路時成爲五條規則)。尊重它。 – 2014-09-24 15:29:09

+0

@ n.m。我不能相信我錯過了這一點。剛剛更改爲'打印輸出(列表&列表)',它的工作原理 - 這是因爲現在缺乏複製構造函數,是正確的? (facepalm,我確實說過這是爲了_practice_吧?)。發佈它作爲答案,我會接受。 :) – Baldrickk 2014-09-24 15:34:31

回答

1
void printout(dll<int> list) 

list是當地的printout。析構函數將在返回時調用。

printout(list); 

這將使用複製構造函數來傳遞參數。

如果你的拷貝構造函數與析構函數不匹配,壞事情將發生。

如果不想執行拷貝構造函數和拷貝賦值運算符與您的課堂上播放,最好是把他們定義爲private沒有實現它們:

template<class T> 
class dll { 
private: 
    dll(dll&);    // no implementation 
    void operator=(dll&); // no implementation 
... 

的編譯器會立即大喊因爲你不小心使用了複製構造函數。

1

它看起來我喜歡printout()是罪魁禍首。您直接傳遞列表,這將調用複製構造函數。除非你明確地定義了一個拷貝構造函數,否則你會得到默認的,它會做一個淺拷貝。我看到在find_node()中引用的指向dllnode的指針。默認的淺拷貝將複製頂級指針,這意味着printout()中的新dll<int>將具有與main()中的外部dllnode相同的有效載荷。當printout()返回時,這些將被刪除,留在main()的外部帶有懸掛指針,從而導致seg錯誤。

快速修復。的printlist()聲明改成這樣:

void printout(dll<int> &list) 

這會通過引用傳遞的名單,這將(一)更快,和(b)將不會調用拷貝構造函數,並導致析構函數被調用。

在任何情況下,你應該創建一個副本costructor,也許一個operator=,而你在這,兩者都需要做一個深拷貝,即複製的dllnode S中的整個列表。有很多這些可能會被使用,在這種情況下,這些默認(淺)方法會導致各種問題。