【问题标题】:Cloning a singly linked list using overloaded assignment operator使用重载赋值运算符克隆单链表
【发布时间】:2023-03-23 21:47:01
【问题描述】:

我正在尝试在 C++ 中实现一个单链表类。我重载了赋值运算符来克隆列表。克隆本身似乎工作正常,但程序在删除克隆列表期间崩溃,这让我怀疑在复制过程中是否有问题。

非常感谢任何帮助。 下面是重载 = 运算符的代码:

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
    }
}

【问题讨论】:

  • 怀疑问题出在复制运算符上。您需要复制列表拥有的每个对象。你能显示代码吗?
  • 调试器是解决此类问题的正确工具。 询问 Stack Overflow 之前,您应该逐行逐行检查您的代码。如需更多帮助,请阅读How to debug small programs (by Eric Lippert)。至少,您应该 [编辑] 您的问题,以包含一个重现您的问题的 Minimal, Complete, and Verifiable 示例,以及您在调试器中所做的观察。
  • 另一个问题(答案没有涉及)是您的赋值运算符附加到当前列表而不是替换它。

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


【解决方案1】:

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

  1. 你没有调用你的复制代码
  2. dd1的地址相同

因此,当您删除第二个列表时,您会删除相同的地址两次。

你在这里真的不需要new

DLList d;
d.push(10);

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

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

如果你想在你的上下文中测试,保持new,改变

d1 = d;

通过

*d1 = *d;  

激活赋值运算符。

另一个建议:分解你的复制代码,以便它在赋值运算符和复制构造函数之间共享,删除代码应该在析构函数和赋值运算符之间共享(以避免两次分配时内存泄漏):未经测试,不要着火如果它无法编译,请告诉我我会修复它,我已经在这里过度回答了:)

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
  }

【讨论】:

  • 哇,谢谢,有帮助!所以,如果我想使用重载赋值运算符和 d1=d,有没有办法做到这一点(我的意思是,我应该在 operator= 函数中进行任何更改吗?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    DLList *d1 = new DLList();
    d1 = d;   // <------------- This line
    d1->display("Cloned list"); //Displays the cloned list fine
    

    在突出显示的行上,您将指针 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
    

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

    【讨论】:

      【解决方案4】:

      你的问题是陈述

      d1 = d;  
      

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

      上面也没有调用DLLists 复制构造函数。如果这是您的意图,您需要这样做

      *d1 = *d;
      

      这使得d1 指向的对象成为d 指向的对象的副本。这两个delete 语句也适用于这种情况。

      【讨论】:

        猜你喜欢
        • 2019-04-02
        • 2016-02-24
        • 1970-01-01
        • 2019-07-30
        • 1970-01-01
        • 1970-01-01
        • 2015-04-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多