使用重载赋值运算符克隆单链表

时间:2016-09-08 08:41:45

标签: c++ linked-list singly-linked-list

我试图在C ++中实现单一链接列表类。我已经重载了赋值运算符来克隆列表。克隆本身似乎工作正常,但程序在删除克隆列表时崩溃,这使我怀疑在复制过程中是否存在错误。

非常感谢任何帮助。 这是overloaded =运算符的代码:

DLList& DLList::operator=(const DLList& iList)
{
    Node *t = iList.head();
    while(t != NULL)
    {
        push(t->data);
        t = t->next;
    }
    return *this;
}

这里是推送操作的代码:

void DLList::push(int data)
{
    Node *n = new Node;
    n->data = data;
    n->next = NULL;
    //n->random = NULL;
    n->next = _head;
    _head = n;
    //cout << "_head" <<_head->data<< "head->next" << (_head->next ? _head->next->data : 0)<<endl;
}

这是主要代码(发生崩溃的地方):

DLList *d = new DLList();
d->push(10);
d->push(20);
d->push(30);
d->push(40);
d->setRandom();
d->display("Original list");  // Displays the original list fine
DLList *d1 = new DLList();
d1 = d;  
d1->display("Cloned list"); //Displays the cloned list fine
delete d;    // works fine
delete d1;  // Crashes here due to segmentation fault, invalid pointer access during delete called as part of destructor)

析构函数代码(如果有帮助):

~DLList()
{
    while(_head != NULL)
    {
        Node *temp = _head;
        _head = _head->next;
        delete temp;  // crashes here during first iteration for the cloned list
    }
}

4 个答案:

答案 0 :(得分:6)

您实际上没有使用赋值运算符!您只是将指针复制到另一个指针。 调试代码(或只是添加跟踪)会显示

  1. 你没有打电话给你的复制码
  2. dd1的地址相同
  3. 因此,当您删除第二个列表时,您会删除相同的地址两次。

    你真的不需要new

    DLList d;
    d.push(10);
    
    DLList d1 = d;  // assignment is REALLY called
    d1.display("Cloned list");
    

    当你超出变量范围时,对象将被删除

    如果您想在上下文中进行测试,请保留new,更改

    d1 = d;
    

    通过

    *d1 = *d;  
    

    激活赋值运算符。

    另一个建议:分解你的拷贝代码,使其在赋值运算符和拷贝构造函数之间共享,并且删除代码应该在析构函数和赋值运算符之间共享(以避免在分配两次时发生内存泄漏):未经测试,不要&#39;如果它没有编译,请点燃我,告诉我我会修复它,我已经在这里过度转移了:))

    DLList& DLList::operator=(const DLList& iList)
    {
       destroy(); // or you'll get memory leaks if assignment done twice
       copy(iList);
       return *this;
    }
    DLList& DLList::DLList(const DLList& iList)
    {
       _head = NULL;   // set _head to NULL in all your constructors!!
       copy(iList);       
    }
    ~DLList()
    {
         destroy();
    }
    
    void DLList::copy(const DLList& iList)  // should be private
    {
        Node *t = iList.head();
        while(t != NULL)
        {
            push(t->data);
            t = t->next;
        }
    }
    void DLList::destroy()  // private
    {
        while(_head != NULL)
        {
            Node *temp = _head;
            _head = _head->next;
            delete temp;  // crashes here during first iteration for the cloned list
        }
       _head = NULL;   // calling destroy twice is harmless
      }
    

答案 1 :(得分:2)

d1 = d;不会复制DLList。相反,您只需将d1指向d指向的列表即可。因此,d1d都指向同一个列表。在程序结束时,您将删除该列表两次,并使程序崩溃。

答案 2 :(得分:2)

如果您希望使用赋值运算符,则需要取消引用指针。见这里:

d1

在突出显示的行上,您将指针 d设置为指向与delete相同的位置。这意味着当您在代码末尾调用d时,您尝试删除同一个对象(在这种情况下为DLList *d1 = new DLList(); *d1 = *d; // <------------- Note the dereference applied to BOTH lists. d1->display("Cloned list"); //Displays the cloned list fine )两次。

要修复代码,您应该取消引用指针:

Warning :-Presenting view controllers on detached view controllers is discouraged

或者,如果可以的话,应该完全避免使用指针。对于您的简单示例,您可以直接创建对象。

答案 3 :(得分:0)

您的问题是声明

d1 = d;  

执行指针赋值,并使d1指向与d相同的对象。随后的delete语句导致相同的对象(*d)被释放两次。这是未定义的行为(其中一个症状就像你描述的那样崩溃)。

上面也没有调用DLList的拷贝构造函数。如果这是你的意图,你需要做

*d1 = *d;

使d1指向的对象成为d指向的对象的副本。在这种情况下,两个delete语句也是合适的。