我使用C++实现了LinkedList,在处理动态分配的内存时,我似乎忘记了一些事情。
我有一个节点类:
class Node {
public:
Node(int d) {
data = d;
next = NULL;
}
Node(int d, Node* n) {
data = d;
next = n;
}
int data;
Node* next;
};
在LinkedList类中,我有以下方法:
void remove(int n) {
Node* current;
current = head;
Node* previous = NULL;
while ( current->data != n && current->next != NULL) {
previous = current;
current = current->next;
}
if (current->data == n) {
previous->next = current->next;
current->next = NULL;
delete current;
}
else {
std::cout << "Node not found" << std::endl;
}
}
我好像忘记了。。当我执行delete current
时,会删除Node
吗?像指针current
所指向的实际对象一样?还是只是删除了指针?或者,使用delete
删除指向动态分配内存的指针会同时删除指针和对象吗?或者我需要为此定义一个Node类析构函数吗?
它只是删除它所指向的结构-在您的例子中是节点-您仍然可以使用该指针-使其指向另一个节点-事实上,由于指针是在堆栈上分配的,因此无法删除指针本身。当您离开该功能时,它会自动"删除"
p.s:无需在空旁边设置current->
删除指向的空闲内存。这有以下含义:
- 您不允许访问此位置的内存(免费后使用)
- Node对象所需的内存量是空闲的,这意味着程序将使用更少的RAM
- 如果您遵循最佳实践并手动将其设置为NULL,则指针本身指向无效位置或NULL
- 对象所在内存位置的数据可以被在该位置具有有效指针的任何其他任务覆盖。因此,从技术上讲,只要没有其他人覆盖Node数据,它仍然保留在内存中
delete p
导致p
指向的对象不存在。这意味着
1、如果对象有析构函数,则调用它;和2.p
变成了一个无效的指针,所以任何试图取消引用它的行为都是未定义的行为。
通常,由所述对象占用的内存再次对程序可用,尽管这实际上是一个实现细节。
短语"删除指针"通常是"删除指针指向的对象"的草率缩写。
假设您已经使用指针上的新删除来分配对象,则执行以下操作:
- 调用对象的析构函数
- 请求内存是空闲的(当这种情况发生时,实际上取决于实现)
在某个时刻,内存管理器将释放内存,并将其标记为进程不可访问。
因此,您可以在调用delete之后将指针设置为约定的值。对于最新的编译器,最好将其设置为nullptr。
它确实删除了current
指向的实际结构。指针保持完好。无需定义析构函数。
delete
运算符将应用于指向对象的指针。指针是通过调用new
分配的堆上内存的地址。内部只有new
分配的地址表。因此,释放这样的内存的关键就是这个地址。在您的情况下,这样的地址存储在类型的变量中,指针指向名为current
的节点。
您的代码中几乎没有问题。有问题的是,您无法判断当前存储的节点是否实际分配在堆上。当前节点可能是在堆栈上分配的。例如
void someFunction(LinkedList &list) {
Node myLocalNode(10);
list.add(&myLocalNode);
list.remove(10); //<-- disaster happens here
}
这同样适用于静态分配的全局变量。
你必须处理极端情况。想想当删除的对象是变量head
指向的第一个对象时会发生什么。通过删除它的内存,您最终会在head
中看到悬空指针,指向未分配的内存或其他人使用的内存。
我的第三个反对意见是写这样的结构。我希望这只是一些学校练习,因为在任何其他情况下,你都应该(几乎必须)使用一些现有的列表,比如C++STL中的std::list
。
无论何时对指针变量调用delete
,它所指向的对象都会从内存中删除,但分配给实际指针变量(在您的情况下,是current
变量)的4个字节,只有当变量超出范围时,即在函数的末尾,这4个字节才会被释放