添加析构函数逻辑会导致分段错误

时间:2014-09-24 15:21:11

标签: c++ segmentation-fault

有几个问题已经有了类似的标题,但是当析构函数 不是 叫。

我一直在编写一个模板化的双链表(为了好玩和练习),到目前为止,它已经正常工作(在整个地方测试插入和删除,测试边缘情况等)。

到目前为止我还没有正确实现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.
}

my(未更改)find_node()函数导致seg-fault。

由于对代码的更改是在析构函数中,因此我假设&#39;它是相关的,但我不知道如何。绝不应该调用析构函数,因为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,
...

2 个答案:

答案 0 :(得分:1)

我认为printout()是罪魁祸首。您直接传递列表,这将调用复制构造函数。除非您已明确定义了复制构造函数,否则您将获得默认值,即执行浅层复制。我看到dllnode中引用find_node()的指针。默认的浅层副本只会复制顶级指针,这意味着dll<int>中的新printout()将与dllnode中的外部main()具有相同的有效负载printout()。当main()返回时,这些将被删除,使printlist()中的外部带有悬空指针,从而导致seg错误。

快速修复。将void printout(dll<int> &list) 的声明更改为:

operator=

通过引用传递列表,这将(a)更快,并且(b)不会调用复制构造函数并导致析构函数被调用。

在任何情况下,您都应该创建一个复制结构函数,并且可能是dllnode,而这两者都需要进行深层复制,即复制整个{{1}列表}}秒。这些可能被使用的地方很多,在这种情况下,这些方法的默认(浅)方法会导致各种各样的问题。

答案 1 :(得分:1)

void printout(dll<int> list)

list位于printout的本地。析构函数将在返回时被调用。

printout(list);

这将使用复制构造函数传递参数。

如果您的复制构造函数与析构函数不匹配,那么Bad Things将会发生。

如果您想在不实现复制构造函数和复制赋值运算符的情况下使用您的类,最好将它们声明为private而不实现它们:

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

一旦您意外使用了复制构造函数,编译器就会对您大吼大叫。