我正在编写一个包含单个链接列表的程序来保存购物清单。每个节点都有项目名称,数量和数量描述(即鸡蛋打)。除了析构函数之外,程序中的所有内容都可以找到。我似乎无法找到它的问题。
驱动程序将执行到代码为return 0;
的末尾,然后调用析构函数并在delete current;
行停止并显示以下消息:
项目14.exe中
0x0FC7A9E8
(msvcr120d.dll
)处的未处理异常:0xC0000005
:访问违规读取位置0xFEEEFEE2
。“。
我已经发布了以下三大功能的实现。默认构造函数将两个指针(first
,last
)初始化为null
,将nodeCount
初始化为0
。
我似乎无法找到问题所在。有什么帮助吗?
List::List(const List& b)
{
Node* newNodePtr = new Node;
Node* nodeCopy = b.first;
newNodePtr = nodeCopy;
first = newNodePtr;
last = newNodePtr;
nodeCount++;
nodeCopy = nodeCopy->getNext();
while (last != b.last)
{
Node* newNode = new Node;
newNode = nodeCopy;
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
nodeCount++;
nodeCopy = nodeCopy->getNext();
}
}
List::~List()
{
Node* current = first;
while (current != nullptr)
{
Node* _next = current->getNext();
delete current;
current = _next;
}
first = nullptr;
last = nullptr;
}
List& List::operator=(const List& rho)
{
Node* current = first;
while (current != nullptr)
{
Node* _next = current->getNext();
delete current;
current = _next;
}
first = nullptr;
last = nullptr;
Node* newNodePtr = new Node;
Node* nodeCopy = rho.first;
newNodePtr = nodeCopy;
first = newNodePtr;
last = newNodePtr;
nodeCount++;
nodeCopy = nodeCopy->getNext();
while (last != rho.last)
{
Node* newNode = new Node;
newNode = nodeCopy;
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
nodeCount++;
nodeCopy = nodeCopy->getNext();
}
return *this;
}
编辑:我还添加了我的push_back函数,因为它写了:
void List::push_back(Node* newNode)
{
if (first == nullptr)
{
first = newNode;
last = newNode;
}
else
{
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
}
nodeCount++;
}
好吧,我想我已经搞清楚了。这段代码似乎有用,它适合我教授提供的驱动程序。下面我列出了三大函数以及他们调用的所有其他函数:
List::List(const List& b)
{
this->copyList(b);
}
List::~List()
{
this->clearList();
}
List& List::operator=(const List& rho)
{
this->clearList();
this->copyList(rho);
return *this;
}
void List::clearList()
{
Node* current = first;
while (current != nullptr)
{
current = pop_front();
delete current;
current = first;
}
first = nullptr;
last = nullptr;
}
void List::copyList(const List& b)
{
first = nullptr;
last = nullptr;
nodeCount = 0;
Node *headNode = b.getFirst();
while (headNode != nullptr)
{
string des = headNode->getDescription();
string qNa = headNode->getQuantityName();
int qNu = headNode->getQuantityNumber();
Node* newNode = new Node(qNu, qNa, des);
push_back(newNode);
headNode = headNode->getNext();
}
}
Node* List::pop_front()
{
Node* saveFirst = first;
first = first->getNext();
nodeCount--;
return saveFirst;
}
void List::push_back(Node* newNode)
{
if (nodeCount == 0)
{
first = newNode;
last = newNode;
}
else
{
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
}
nodeCount++;
}
答案 0 :(得分:0)
至少部分取决于调用析构函数时第一个指向的内容。
您的代码未复制节点的内容。相反,它只是操纵指针,正如dyp指出的那样,你有一个泄漏: Node * newNodePtr = new Node; Node * nodeCopy = b.first; newNodePtr = nodeCopy;
您可能想要了解复制交换习惯用法。 What is the copy-and-swap idiom?
答案 1 :(得分:0)
这可能无法解决您的确切问题,但如果您拥有您提到的功能,那么复制构造函数的伪代码将如下所示。
List::List(const List& b)
{
Node *headNode = b.getHeadNode();
while (headNode != NULL)
{
push_back(headNode->getDataFromNode());
headNode = headNode->getNextNode();
}
}
基本上,简而言之就是整个复制构造函数。您基本上是从列表中的第一个节点开始,从该节点获取数据,并调用push_back()来添加新数据。我假设push_back()完成了创建节点,向其添加数据以及将其正确放置在列表后面的所有棘手工作。
请注意此实现的小巧,紧凑和直观。我们知道创建链表副本所需要做的就是首先确保列表为空(用于新对象),并继续将旧列表中的项添加到新列表中。由于push_back()将一个项添加到列表中(并且具有创建节点并将其链接到末端节点的所有复杂性),因此我们以智能方式使用它来创建副本。
请注意,您还需要一个赋值运算符来与复制构造函数一起使用。示例中的赋值运算符只是在继续之前调用clear()(如果你有这样的函数)来删除所有节点。
请记住,所有这些都要求您的push_back()函数完美运行。它应该知道如何在空和非空列表的末尾正确处理插入。
编辑:
如果你的驱动程序代码确实在push_back之前创建了新节点(因此push_back没有分配新节点),那么可以使用替代代码:
List::List(const List& b)
{
Node *headNode = b.getHeadNode();
while (headNode != NULL)
{
Node *newNode = new Node(headNode->getDataFromNode());
push_back(newNode);
headNode = headNode->getNextNode();
}
}
在备用版本中,我假设可以使用数据作为参数创建新节点的构造函数。我个人不喜欢让push_back()的设计没有完成创建节点的所有工作,但这是另一个问题。