问题
So, I'm trying override operator= in a linked list class I'm writing, but keep getting this weird problem for some reason.
List& List::operator=(const List& copyList){
if(copyList.head != nullptr){
makeEmpty(); // clears *this from any previous nodes
cout << "if statement " << endl;
head = new Node; // create a new node for head
head -> data = copyList.head -> data; // copy the first data of copylist
Node* pnew = head; // a temp node to traverse the new linkedlist
assert(head != nullptr);
Node* current2 = copyList.head;
current2 = current2 -> next;
while(current2 != NULL && pnew != NULL){
cout << "entering while loop " << endl;
pnew-> next = new Node;
pnew -> next->data = current2 ->data;
cout << "pnew next data " << *(pnew -> next->data) << endl;
assert(pnew-> next != nullptr);
pnew = pnew -> next;
current2 = current2 -> next;
cout << "after current2" << endl;
}
pnew -> next = NULL;
}else{
cout << "else statement " << endl;
head = nullptr;
}
cout<< "printing out copylist"<< endl << copyList << endl;
cout<< "printing current list: " << endl << *this << endl;
return *this;
}
So, this is the code I have to test the operator override:
cout << "mylist:" << endl << mylist << endl;
cout << "mylist4:" << endl << mylist4 << endl;
mylist = mylist4;
cout << "mylist:" << endl;
cout << mylist << endl;
cout << "mylist4:" << endl;
cout << mylist4 << endl;
This is the output:
mylist:
10 f
16 u
20 n
25 !
mylist4:
14 s
15 t
16 u
18 f
19 f
25 !
if statement
entering while loop
pnew next data 15 t
after current2
entering while loop
pnew next data 16 u
after current2
entering while loop
pnew next data 18 f
after current2
entering while loop
pnew next data 19 f
after current2
entering while loop
pnew next data 25 !
after current2
printing out copylist
14 s
15 t
16 u
18 f
19 f
25 !
printing current list:
14 s
15 t
16 u
18 f
19 f
25 !
*crashes right here*
I have been trying to figure out this problem for about 3 days. Any help would be highly appreciated. Thanks in advance!
EDIT: here are the constructor (the destructor is the default one by the compiler):
NodeData::NodeData(int n, char c) {
num = n; ch = c;
}
EDIT2: I found the problem after careful examination. The problem was that I did not point the last node of head, pnew after the while loop, to null. This fixed the issue. Thanks to all your support.
回答1:
You have control over your Node class, and it should support copy-construction and assignment operators either transparently or if needed by your own hand. Given that, I concur with Dietmar that utilizing the copy-ctor/swap/destructor mechanics is the ideal approach.
If you insist on doing this by-hand the following is one way to do it. Your implementation is making this much harder than it needs to be.
List& List::operator=(const List& copyList)
{
List tmp;
Node **dst = &tmp.head;
const Node* src = copyList.head;
while (src)
{
*dst = new Node(*src); // invoke Node copy-ctor
src = src->next; // advance source
(*dst)->next = nullptr; // break link to original next
dst = &(*dst)->next; // move target to next pointer
}
// tmp now has a copy of the source list
// swap its head pointer with ours.
std::swap(tmp.head, head);
// upon return, the tmp object that now holds our
// original list will clean it up. we own the
// new list form this point on.
return *this;
}
How It Works
The pointer-to-pointer dst
always holds the address of the next pointer to be populated with a new node. Initially it holds the address of the local List tmp
object's head pointer. . As we add nodes it is updated to always hold the address of the next
pointer of the last-node-added. By doing this we get forward-chaining automatically. Once we're done copying, tmp
is now a duplicate of the source. We then swap head pointer with own own. This in turn swaps who owns which list. When tmp
is destroyed on function-exit it will destroy our old list with it. We keep the new list, pointed to by our head
pointer.
The above assumes you're using the default copy-ctor of Node
, which means making a copy will copy both the data value and the next link, the latter you do NOT want, thus the embedded link-break. If you actually implement Node::Node(const Node&)
to always set the link to NULL after the copy the link-break can be eliminated. I.e., your Node
copy-constructor should look something like this:
Node::Node(const Node& arg)
: data(arg.data)
, next()
{
}
This ensures a clean(as clean as data
can be copied, anyway) copy with no accidental link to the original arg
next pointer.
All of that said, Dietmar's answer is the most-correct, and I've up voted it accordingly. If this helps you, consider up-voting it, but his is easily the ideal solution.
回答2:
Without looking to hard at the implementation (I stopped when I spotted makeEmpty()
which makes sure that the assignment operator won't be strongly exception-safe without any good reason): the easiest way to implement an assignment operator is to leverage the copy constructor, the destructor, and a generally trivial to write swap()
funciton. The implementation look the same for all classes except for the class name:
T& T::operator= (T other) {
other.swap(this);
return *this;
}
Note, it is deliberate to pass the argument by value: this is where the value is actually happening. Compared to copying by T const&
it has the advantage the actual copy can be elided in some cases.
来源:https://stackoverflow.com/questions/19610604/override-operator-linked-linked-c-deep-copy