c++ - 如何在 c++ 的析构函数中为合并的 LL 正确释放内存?
问题描述
我创建了一个包含一些操作的链接列表类。
我正在尝试将两个链表合并在一起,如main
函数所示。我能够成功地执行该操作并将其显示在屏幕上。
next
不过,我怀疑在实现尾节点的指针时我可能做错了什么。当调用析构函数时,我打开调试器以查看到底发生了什么。它成功删除了所有节点,并显示old->next
并随后head
最终等于nullptr
。我确保析构函数仅在空操作为 false for 时循环nullptr
。
但是,由于某种原因,析构函数继续循环,程序给了我错误:
LinkedList(2000,0x1000d3dc0) malloc: 对象 0x1007239d0 的错误: 被释放的指针未被分配
我知道解决方案可能很明显,但我完全被大便了。析构函数适用于非合并列表。
class Node{
public:
int data;
Node* next;
friend class LinkedList;
};
class LinkedList{
public:
Node* head;
public:
LinkedList()
{head = nullptr;}
~LinkedList()
{while (!empty()) remove();}
void addDataBack(int data);
void display();
void remove();
bool empty() const
{return head == nullptr;}
void merge(Node* list1, Node* list2);
};
void LinkedList::addDataBack(int data){
Node *p = new Node;
Node *t;
t = head;
p->data = data;
p->next = nullptr;
if (!head){
head = p;
}
else{
t = head;
while(t->next){
t = t->next;
}
t->next = p;
}
}
void LinkedList::display(){
Node *t = head;
while (t){
cout << t->data << endl;
t = t->next;
}
}
void LinkedList::remove(){
Node *old = head;
head = old->next;
delete old;
}
void LinkedList::insertNode(int index, int data){
Node *node = new Node;
int i = 0;
Node *t = head;
Node *p = nullptr;
node->data= data;
while ( t!= NULL){
if (index == i){
p->next = node;
node->next = t;
break;
}
p = t;
t = t->next;
i++;
}
}
void LinkedList:: merge(Node *list1, Node *list2){
Node* t = list1;
head = list1;
while (t->next) {
t = t->next;
}
t->next = list2;
}
int main(int argc, const char * argv[]) {
LinkedList list;
LinkedList list2;
list.addDataBack(8);
list.addDataBack(3);
list.addDataBack(7);
list.addDataBack(12);
list.addDataBack(9);
list.insertNode(2, 25);
list2.addDataBack(4);
list2.addDataBack(10);
LinkedList list3;
list3.merge (list.head, list2.head);
list.display();
return 0;
}
解决方案
代码无法编译,因为您在类定义中缺少插入函数原型。
见 insertNode 函数;在该行
p->next = node
中,如果 index 为 0,则该行将间接指向一个空指针并引发异常。- 如果您提供的索引超出当前节点数 - 1,则insertNode 函数将泄漏内存
- 如果当前列表为空,insertNode 函数会泄漏内存
这是它的外观。
void LinkedList::insertNode(int index, int data)
{
Node* newNode = new Node;
newNode->data = data;
//Wrap this up quick if the list is already empty.
if (head == nullptr)
{
head = newNode;
return;
}
int i = 0;
Node* current = head;
Node* prev = nullptr;
while (current != nullptr)
{
if (index == i)
{
newNode->next = current;
if (prev)
prev->next = newNode;
return;
}
prev = current;
current = current->next;
i++;
}
//if (index >= i)
//Either delete the new node, or throw an out of bounds exception.
//Otherwise this will result in a memory leak. Personally, I think
//throwing the exception is correct.
delete newNode;
}
这是主要问题:
您的合并函数有点令人困惑,因为您实际上是从两个列表创建一个新列表,但不是通过构造函数,而是简单地合并它们。这将意味着 list1 在功能上等同于 list3,但地址都是混合的。这意味着当我们退出主函数作用域时,您将从 list1 中删除内存,然后当它销毁 list2 时,它也会再次删除它们,而 list3 也会这样做(尽管在此之前它会崩溃)。
为什么不简单地让它取一个列表,然后将两者合并呢?
#include <iostream>
#include <string>
using namespace std;
class Node{
public:
int data;
Node* next;
friend class LinkedList;
};
class LinkedList{
public:
Node* head;
public:
LinkedList()
{head = nullptr;}
~LinkedList();
void addDataBack(int data);
void display();
void remove();
void insertNode(int index, int data);
bool empty() const
{return head == nullptr;}
void merge(LinkedList& otherList);
};
LinkedList::~LinkedList()
{
while (!empty())
remove();
}
void LinkedList::addDataBack(int data){
Node *p = new Node;
Node *t;
t = head;
p->data = data;
p->next = nullptr;
if (!head){
head = p;
}
else{
t = head;
while(t->next){
t = t->next;
}
t->next = p;
}
}
void LinkedList::display(){
Node *t = head;
while (t){
cout << t->data << endl;
t = t->next;
}
}
void LinkedList::remove(){
Node *old = head;
head = old->next;
delete old;
old = nullptr;
}
void LinkedList::insertNode(int index, int data)
{
Node* newNode = new Node;
newNode->data = data;
//Wrap this up quick if the list is already empty.
if (head == nullptr)
{
head = newNode;
return;
}
int i = 0;
Node* current = head;
Node* prev = nullptr;
while (current != nullptr)
{
if (index == i)
{
newNode->next = current;
if (prev)
prev->next = newNode;
return;
}
prev = current;
current = current->next;
i++;
}
//if (index >= i)
//Either delete the new node, or throw an out of bounds exception.
//Otherwise this will result in a memory leak. Personally, I think
//throwing the exception is correct.
delete newNode;
}
void LinkedList:: merge(LinkedList& otherList){
Node* thisTail = head;
while (thisTail->next) {
thisTail = thisTail->next;
}
thisTail->next = otherList.head;
otherList.head = nullptr;
}
int main(int argc, const char * argv[]) {
LinkedList list;
LinkedList list2;
list.addDataBack(8);
list.addDataBack(3);
list.addDataBack(7);
list.addDataBack(12);
list.addDataBack(9);
list.insertNode(2, 25);
list2.addDataBack(4);
list2.addDataBack(10);
list.merge(list2);
list.display();
list2.display();
cout << "list2 is " << (list2.empty() ? "empty." : "not empty");
return 0;
}
最后注:
尽量避免使用单字母变量,除非它们用于迭代,否则(尤其是链表和指针杂耍)很难维护、调试和获得帮助。
推荐阅读
- docker - 访问其中一个容器内的 docker shell
- javascript - 没有在 Django Python 中加载静态文件
- scilab - 如何在 Scilab 应用程序中更改参数值后刷新图形?
- javascript - 模拟 isTrusted=true 到事件监听器 (mouseEvent)
- android - 我的手机未收到消息的 Firebase reg id,仅在模拟器上
- c++ - 在可变参数模板上下文中缺少有关格式说明符的警告
- powershell - Wix 安装程序自定义操作 powershell 脚本未正确执行
- git - 源树无法推送
- .net - 在安装程序项目中使用自定义操作提交 .exe 失败
- python-3.x - 如何在 Odoo 11 中覆盖自定义模型的更新功能