>我正在尝试从链表中删除重复项,并遇到了一个问题,这可能是显而易见和直接的,但是我已经很多年没有使用C++
了,我无法通过阅读SO上的类似问题来找出我做错了什么。
下面是我的部分代码。我删除了不相关的部分(例如构造函数、其他方法等(。
template<class T>
class Node {
Node() : data(NULL), next(NULL), prev(NULL) {}
explicit Node(T d) : data(d), next(NULL), prev(NULL) {}
explicit Node(T d, Node<T> *nxt, Node<T> *prv) : data(d), next(nxt), prev(prv) {}
~Node() { delete next; delete prev; }
T data;
Node *next;
Node *prev;
};
template<class T>
class LinkedList {
LinkedList() : head(NULL) {}
explicit LinkedList(Node<T> *head_node) : head(head_node) {}
LinkedList& operator=(const LinkedList ©_list);
~LinkedList(); //didn't implement this but I guess delete head is all?
Node<T>* head;
};
template<class T>
LinkedList<T> RemoveDuplicates(const LinkedList<T> &linked_list) {
//my = overload creates a whole new list with the same data as the original one
LinkedList<T> result = linked_list;
Node<T> *cur = result.head;
Node<T> *prev = NULL;
while (cur) {
if (...) { //duplicate found
Node<T> *temp = cur;
prev->next = cur->next;
cur = cur->next;
cur->prev = prev;
free(temp); //Here is my problem!!!
}
else {
prev = cur;
cur = cur->next;
}
}
return result;
}
所以首先,我做了delete temp
,我得到了Segmentation fault
.然后我意识到你只delete
你new
的东西.很公平,但是在main
中构建整个列表时,我正在new
每个Node
:
Node<char> *h = new Node<char>('H'); //I have a constructor to handle this
Node<char> *e = new Node<char>('E');
Node<char> *l1 = new Node<char>('L');
Node<char> *l2 = new Node<char>('L');
Node<char> *o = new Node<char>('O');
h->next = e;
h->prev = NULL;
e->next = l1;
e->prev = h;
//and so on
那么为什么不允许我delete
在其他地方new
的东西呢?是因为它在当前范围之外new
吗?
其次,free
空间工作正常,但显然不是正确的做法,因为我没有malloc
而是new
编辑!
我做错了什么?如何正确杀死已删除的节点?
编辑1:根据对我的帖子的回复使其更具描述性编辑2:添加了 3 个方法的规则
如果您分配了内存new
,则无法使用free()
您可以将malloc()
与free()
一起使用,也可以将new
与delete
一起使用。 您也不应该对对象使用 malloc()
和 free()
,因为它们不会调用对象构造函数和析构函数,这与 new
和 delete
因此,由于您分配了具有new
节点,因此我们可以更改
free(temp); //Here is my problem!!!
自
delete temp;
这是对其他答案的补充,可以正确识别和解决OP的直接问题。需要快速浏览来解释接下来会发生什么。
Node<T> *temp = cur;
prev->next = cur->next;
cur = cur->next;
cur->prev = prev;
此时 temp 尚未清除,因此 next 和 prev 仍然指向曾经围绕 cur 的两个节点,现在链接在一起。如果不是因为以下原因,这将导致严重的问题:
free(temp); //Here is my problem!!!
free
失败,因为 temp 指向的值是由 new 分配的。内森·奥利弗(NathanOliver(的答案已经涵盖了这一点,但潜伏在下面会发生什么
delete temp;
这将调用析构函数像一个好的小对象一样进行清理。不幸的是,Node
析构函数如下所示:
~Node() { delete next; delete prev; }
temp->next
仍然指向活动节点。temp->prev
也是如此。
下一个和上一个得到delete
d。它调用它们的析构函数,delete
他们接触的两个节点,并调用死亡风暴,这将摧毁列表中的所有节点。
如果双重删除不先杀死程序。每个链接节点将尝试delete
刚刚deleted
它们的节点。不好。
很有可能Node
析构函数应该照顾好自己,而将其他Node
的销毁留给LinkedList
类。
那么为什么不允许我删除某处新的东西 还?是因为它是在当前范围之外更新的吗?
你可以delete
new
其他地方的东西。只要您指向有效的地址,这不是问题。然后,当您尝试使用指向现在delete
的地址的指针之一时,会出现赛段错误。
其次,释放空间效果很好,但我觉得这就像作弊!
您应该只在使用malloc
时使用free
,在使用new
时delete
。你永远不应该把它们混为一谈。另外,在delete
之后,养成将NULL
分配给指针变量的习惯。
因为我可能需要在我的节点的析构函数上完成一些事情。
封装数据的类也应该在内部执行自己的内存管理。例如,LinkedList
应管理节点的内存。
这意味着,如果你有类似于LinkedList<T>::add(Node<T> *data)
的东西,你应该考虑类似LinkedList<T>::add(T data)
的东西;然后列表类本身负责处理节点。
否则,您将冒着将new
ed节点发送到列表中的风险,然后列表在某个时候在内部delete
它,但在其他地方,列表的客户端仍然持有对现在无效的节点的引用。当客户端尝试使用它时,砰!:再次出现段错误。
我做错了什么?如何正确杀死已删除的节点?
除了将 new
s 与 free
s 混合在一起之外,段错误很可能是由无效的内存访问触发的,因此您应该找到不再指向有效(即 un deleted
(内存的指针。
在这种情况下,拥有构造函数和析构函数的代码非常重要。
默认情况下,您应该在构造函数中使用"new",在析构函数中使用"delete"。更具体地说,在构造函数中分配所需的所有资源并在析构函数中解除分配非常重要,这样可以确保没有任何内存泄漏。
free(temp);//You don't need this, also you don't need delete.
请发布您的构造函数和析构函数代码。
L.E:
我认为你应该做这样的事情:
template<class T>
class LinkedList
{
private:
struct Node {
T m_data;
Node *m_next;
Node *m_prev;
};
Node* m_first;
Node* m_last;
int m_count;
public:
LinkedList();
~LinkedList();
T GetFirst();
T GetLast();
void AddNode(T node);
void RemoveAt(int index);
T GetAt(int index);
int Count();
};
//constructor
template <typename T>
LinkedList<T>::LinkedList(){
m_count = -1;//-1 == "The list is empty!
}
template <typename T>
void LinkedList<T>::AddNode(T data){
Node* temp = new Node; //new is called only here -> delete is called in destructor or in RemoveAt
temp->m_data = data;
if (m_count > -1){//If the list contains one or more items
temp->m_next = m_first;
temp->m_prev = m_last;
m_last->m_next = temp;
m_last = temp;
m_count++;
}
else if (m_count == -1)
{//If no items are present in the list
m_first = temp;
m_first->m_next = temp;
m_first->m_prev = temp;
m_last = temp;
m_last->m_next = temp;
m_last->m_prev = temp;
m_count = 0;
}
}
template <typename T>
void LinkedList<T>::RemoveAt(int index){
if (index <= m_count)//verify this request is valid
{
Node* temp = m_last;
for (int i = 0; i <= index; ++i){
temp = temp->m_next;
}
temp->m_prev->m_next = temp->m_next;
temp->m_next->m_prev = temp->m_prev;
delete temp;
m_count--;
}
}
template <typename T>
T LinkedList<T>::GetAt(int index)
{
Node* temp = m_first;
if (index <= m_count && index > -1){
int i = 0;
while(i < index){
temp = temp->m_next;
i++;
}
}
return temp->m_data;
}
template <typename T>
T LinkedList<T>::GetFirst() {
return m_first->data;
}
template <typename T>
T LinkedList<T>::GetLast(){
return m_last->data;
}
template <typename T>
int LinkedList<T>::Count(){
return m_count;
}
template <typename T>
LinkedList<T>::~LinkedList(){
while(m_count > -1){
Node* temp = m_first;
m_first = temp->m_next;
delete temp;//delete in destructor
m_count--;
}
}
有很多答案表明new/delete
和malloc()/free()
应该总是配对,但我觉得应该说出确切的原因。
就像您的初始化一样,
Node<char> *h = new Node<char>('H');
new
关键字返回在语句本身中定义的完全类型的对象对象指针,而 malloc()
只是分配给定的内存空间而不分配类型,因此返回一个void*
。此外,new/delete
处理免费存储中的内存,同时malloc()/free()
堆上分配/释放内存,尽管某些编译器在某些情况下可能会在malloc()/free
方面实现new/free
。
同样重要的是,new
和delete
分别调用构造函数和析构函数,而malloc()
和free()
只是分配和释放堆内存,而不考虑内存本身的状态。