简体   繁体   中英

Cloning a singly linked list using overloaded assignment operator

I'm trying to implement a Singly linked list class in C++. I've overloaded the assignment operator to clone the list. The cloning itself seems to work fine, but the program crashes during deletion of the cloned list, which leads me to suspect if something's wrong during the copy procedure.

Any help is highly appreciated. Here's the code for the overloaded = operator:

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

Here's the code for the push operation:

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

Here's the main code (where the crash happens) :

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)

Code for destructor (if it helps):

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

you are not using the assignment operator actually! You are just copying the pointer to the other pointer. Debugging your code (or just add traces) would have showed that

  1. you were not calling your copy code
  2. the addresses of d and d1 are the same

So when you delete the second list you delete the same address twice.

You don't really need new here:

DLList d;
d.push(10);

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

the objects will be deleted when you go out of scope of your variables

If you want to test in your context, keeping the new , change

d1 = d;

by

*d1 = *d;  

to activate assignment operator.

Another advice: factorize your copy code so it is shared between assignment operator and copy constructor, and the deletion code should be shared between destructor and assignment operator (to avoid memory leaks when assigning twice): Not tested, don't flame me if it doesn't compile, just tell me I'll fix it, I'm already overanswering here :)

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; is NOT copying DLList. Instead, you just make d1 point to the list that d pointing to. So, both d1 and d are pointing to the same list. At the end of your program, you will delete the list twice, and make your program crash.

If you wish to use the assignment operator, you need to dereference your pointers. See here:

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

On the highlighted line, you're setting the pointer d1 to point to the same location as d . This means that when you call delete at the end of your code, you're trying to delete the same object ( d in this case) twice.

To fix your code, you should dereference your pointers:

DLList *d1 = new DLList();
*d1 = *d;   // <------------- Note the dereference applied to BOTH lists.
d1->display("Cloned list"); //Displays the cloned list fine

Alternatively, you should avoid using pointers entirely if you can. For your trivial example you could simply create your objects directly.

Your problem is the statement

d1 = d;  

which does a pointer assignment, and makes d1 point to the same object as d . The subsequent delete statements cause the same object ( *d ) to be released twice. That is undefined behaviour (of which one symptom is a crash like you describe).

The above also does not invoke DLList s copy constructor. If that is your intent, you need to do

*d1 = *d;

which makes the object pointed to by d1 a copy of the one pointed to by d . The two delete statements are also appropriate in this case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM