我试图在C ++中创建自己的基本单链表作为学习练习,而且我在内存管理部门遇到了一些困难。目前我有......
'Node'类:
class Node
{
public:
char *value;
Node *next;
Node();
~Node();
};
Node::Node()
{
}
Node::~Node()
{
delete[] value;
}
然后我的列表(为了简洁,我省略了某些方法调用):
class LinkedList
{
private:
Node *head;
public:
LinkedList();
~LinkedList();
void Add(char **x);
};
LinkedList::LinkedList()
{
head = 0;
}
LinkedList::~LinkedList()
{
Node *temp;
Node *current = head;
while(current)
{
temp = current;
current = current->next;
delete temp;
}
}
void LinkedList::Add(char **x)
{
Node *nodeToAdd = new Node();
nodeToAdd->value = *x;
nodeToAdd->next = NULL;
Node *current = head;
if(!head)
{
head = nodeToAdd;
return;
}
while(current->next)
{
current = current->next;
}
current->next = nodeToAdd;
}
我正在尝试按如下方式使用此代码(为了简洁起见,我省略了一些内容):
int main()
{
LinkedList *list = new LinkedList();
char *alpha = "alpha";
char *beta = "beta";
char *charlie = "charlie";
char *delta = "delta";
char *echo = "echo";
list->Add(&alpha);
list->Add(&beta);
list->Add(&charlie);
list->Add(&delta);
list->Add(&echo);
delete list;
}
主要删除列表的最后一次调用会产生错误:
Debug Assertion Failed! Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
我在这里做错了什么?
答案 0 :(得分:7)
各种Node::value
指向的数据不会动态分配,因此您不应删除它们。应用“所有权”的概念,节点应该创建自己的数据副本,他们拥有并可以删除,或者节点不拥有数据,因此他们不应该负责删除它。
您还可以使用引用计数实现多个所有权,例如Objective-C(请参阅Objective-C Memory Management Rules以获取更多信息),但您必须小心避免所有权周期。您经常在第三方智能指针中找到某种类型的引用计数,例如Boost's smart_ptr library。由于您是为了学习体验而这样做,因此使用库比使用库更有意义。当然,你现在也可以使用一个库,让你专注于你想要学习的东西。
有一天,一个学生来到Moon并说:“我理解如何制作一个更好的垃圾收集器。我们必须保留每个缺点指针的引用计数。“ 月亮耐心地告诉学生以下的故事:“有一天,一个学生来到Moon说:'我明白如何制作一个更好的垃圾收集器......
答案 1 :(得分:2)
您正在尝试释放未在堆上分配的内存。
char *alpha = "alpha"; --- not allocated on heap
在delete[]
析构函数中调用Node
会导致堆损坏。
有些观点:
1)在构造函数中正确初始化指针:
Node::Node():value(NULL),next(NULL)
{
}
2)拥有价值所有权。
答案 2 :(得分:1)
如果它不是由new运算符创建的,则不应释放指针使用delete [] / delete。 delete []操作有一些操作,例如从托管池中释放/回收标记的内存。由于您的指针不属于这些东西,因此会出现问题。恕我直言,底层的delete []代码是_BLOCK_TYPE_IS_VALID(pHead-> nBlockUse)的东西。
答案 3 :(得分:0)
问题是你假设你可以删除节点内的数据,但是你传递了指向字符串文字的指针,而你无法删除。
如果您假设Node对象控制其中数据的生命周期,则Node构造函数或LinkedList中的Add函数必须复制正在传递的数据。
答案 4 :(得分:0)
在析构函数中,您尝试对删除(删除[])静态字符串进行数组删除。您已更改添加功能以保留字符串并首先将其复制。请参阅下面的代码。
但是,如果我是你并且对内存管理还不熟悉,我真的会使用类似CString而不是原始的“char *”,因为它更容易处理。
void LinkedList::Add(const char *x)
{
Node *nodeToAdd = new Node();
int len=strlen(x);
nodeToAdd->value = new char [len+1]; // room for string + terminating 0
strcpy(nodeToAdd->value,x);
nodeToAdd->next = NULL;
Node *current = head;
if(!head)
{
head = nodeToAdd;
return;
}
while(current->next)
{
current = current->next;
}
current->next = nodeToAdd;
}
答案 5 :(得分:-1)
值和下一个没有分配内存。你应该在Node的构造函数中分配内存。