我一直在让双链表的赋值运算符正常工作时遇到问题。当rhs是一个空列表时,运算符可以正常工作,但如果填充了它,它就不起作用,并抛出一个异常错误,称为"读取访问冲突"。
这是我不会运行的主要例程。
#include <cstdlib>
#include "linkedlist.h"
using namespace std;
int main()
{
LinkedList e1;
e1.insertToFront("A");
e1.insertToFront("B");
e1.insertToFront("C");
LinkedList e2;
e2.insertToFront("Please work");
e1 = e2; //Expecting e1 to now just hold "Please work".
system("pause");
}
这是赋值运算符本身(在一个单独的头文件中(。
// assignment operator
const LinkedList& LinkedList::operator=(const LinkedList& rhs)
{
Node* temp;
temp = head;
Node* forward;
forward = new Node;
while (temp != nullptr) // clear memory of target variable
{
forward = temp->next;
delete temp;
temp = forward;
}
if (rhs.head == nullptr)
{
head = nullptr;
tail = nullptr;
}
//GOOD THROUGH THIS PNT.
else
{
temp = rhs.head;
while (temp != nullptr)
{
this->addToEnd(temp->value);
temp = temp->next;
}
}
return *this;
}
这是我调用的addToEnd函数以及Node结构。
void LinkedList::addToEnd(const ItemType& val)
{
Node* temp;
temp = new Node;
temp->value = val;
if (this->head == nullptr)
{
head = temp; // make new node head if list is empty
head->next = nullptr;
head->prev = nullptr;
tail = temp;
}
else
{
temp->prev = tail; // otherwise point current tail towards temp
temp->next = nullptr;
tail->next = temp;
tail = temp;
}
return;
}
////////////////////////////////////////////////////////////////////////
struct Node
{
ItemType value;
Node* next;
Node* prev;
};
您删除了旧节点,但忽略了将head
和tail
设置为nullptr
,因此这些指针仍然指向已删除的对象。然后尝试将元素添加到已删除的列表中,并获得"未定义的行为"。
清除现有节点时,在开始从源列表中复制值之前,不会将head
和tail
指针重置为nullptr
。因此,您正在使用无效指针将新的Node
添加到列表中。
您也有一个小的内存泄漏,因为您正在为forward
变量分配一个新的Node
,然后如果源列表不为空,则立即重新分配forward
以指向另一个Node
。您永远不会delete
您使用new
分配的Node
。在清除现有节点时,不应该分配任何东西。
为了更安全一点,您应该将列表的清除封装在一个单独的方法中,然后您可以在需要时调用该方法(不要忘记在析构函数中,而不仅仅是在赋值operator=
中(。
如果您使用复制和交换习惯用法在复制构造函数(您确实有一个,对吧?(方面实现您的赋值operator=
,那就更好了。既然您显然使用的是C++11或更高版本,那么您也应该实现一个move构造函数。这些步骤将极大地简化operator=
的实现,并使其使用更安全。
试试这个:
class LinkedList
{
public:
LinkedList() = default;
LinkedList(const LinkedList& src);
LinkedList(LinkedList&& src);
~LinkedList();
...
LinkedList& operator=(LinkedList rhs);
...
private:
Node *head = nullptr;
Node *tail = nullptr;
...
};
#include <utility>
LinkedList::LinkedList(const LinkedList& src)
{
Node* temp = src.head;
while (temp)
{
addToEnd(temp->value);
temp = temp->next;
}
}
LinkedList::LinkedList(LinkedList&& src)
: head(src.head), tail(src.tail)
{
src.head = src.tail = nullptr;
}
LinkedList::~LinkedList()
{
clear();
}
void LinkedList::clear()
{
Node *temp = head;
head = tail = nullptr;
while (temp)
{
Node *forward = temp->next;
delete temp;
temp = forward;
}
}
LinkedList& LinkedList::operator=(LinkedList rhs)
{
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
您也可以稍微简化insertToFront()
和addToEnd()
方法:
struct Node
{
ItemType value;
Node* next = nullptr;
Node* prev = nullptr;
Node(const ItemType& val) : value(val) {}
};
void LinkedList::insertToFront(const ItemType& val)
{
Node* temp = new Node(val);
if (!tail)
tail = temp; // make new node tail if list is empty
if (head)
{
temp->next = head; // point current head towards temp
head->prev = temp;
}
head = temp;
}
void LinkedList::addToEnd(const ItemType& val)
{
Node* temp = new Node(val);
if (!head)
head = temp; // make new node head if list is empty
if (tail)
{
temp->prev = tail; // point current tail towards temp
tail->next = temp;
}
tail = temp;
}