Def*_*ult 1 c++ list heap-memory stack-memory
我最近收到了一份数据结构课程的大学作业,要求我用 C++ 创建一个双向链表。
在处理双向链表时,我需要实现各种功能,但特别引起我注意的一种方法是“clear()”。该方法负责清除双向链表内的所有元素:
void clear(Node* head_ptr)
{
Node* previous_ptr = nullptr;
while(head_ptr != nullptr)
{
previous_ptr = head_ptr; // Store previous node.
head_ptr = head_ptr->next;
delete previous_ptr;
}
};
Run Code Online (Sandbox Code Playgroud)
该方法非常简单;它只是迭代所有元素并为每个元素释放内存Node。然后,我在析构函数中调用此方法,如下所示:
~List()
{
clear_list(m_head_ptr);
};
Run Code Online (Sandbox Code Playgroud)
然后我开始思考。如果我的节点元素位于堆上,这种释放内存的方法就很好,如下所示:
int main()
{
List list;
Node* node_1 = new Node(3, nullptr); // The tail node.
Node* node_2 = new Node(1, node_1);
Node* node_3 = new Node(5, node_2);
Node* node_4 = new Node(7, node_3); // The head node.
list.add(node_1);
list.add(node_2);
list.add(node_3);
list.add(node_4);
// Then do some stuff with the list...
} // The list goes out of scope and the destructor is called...
Run Code Online (Sandbox Code Playgroud)
但是,一旦我在堆栈上创建 s并将Node指针传递给堆栈对象,这种情况就会中断,如下所示:
int main()
{
List list;
Node* node_1(3, nullptr); // The tail node.
Node* node_2(1, node_1);
Node* node_3(5, node_2);
Node* node_4(7, node_3); // The head node.
list.add(&node_1);
list.add(&node_2);
list.add(&node_3);
list.add(&node_4);
// Then do some stuff with the list...
} // The list goes out of scope and the destructor is called and the program crashes because it attempts to free stack objects...
Run Code Online (Sandbox Code Playgroud)
原因是因为我试图释放堆栈对象,这不是一个好主意。当然,我们通常不会使用基于堆栈Node的,因为我们通常希望Node数据在它们创建的范围之外持续存在。尽管如此,这还是引出了我的问题:
我该如何应对?有没有办法检查内存中的某些内容是否Node在我的函数中的堆或堆栈上,然后相应地释放它?或者,有更好的方法来解决这个问题吗?
最简单、最有效的解决方案通常是让列表自行管理节点的分配。用户甚至不需要知道类型的node存在,更不用说分配它们等等。
因此,对于用户来说,您在问题中显示的内容相当于以下内容:
List list;
list.add_head(1);
list.add_head(3);
list.add_head(5);
list.add_head(7);
Run Code Online (Sandbox Code Playgroud)
您通常还需要将add_tail项目添加到列表末尾。每个add(头或尾)通常应该返回一个抽象iterator类型(这可能是指向节点的指针的包装器),因此您可以执行以下操作:
auto pos = list.add_head(7);
list.add_tail(5);
list.add_after(pos, 3);
Run Code Online (Sandbox Code Playgroud)
...这会在开头添加 7,在结尾添加 5,并3在7.
这样,列表本身就会分配所有节点,并知道如何处理它们。您可以更进一步,将分配和处置委托给一个Allocator类。这当然很有用,但可能有点超出了基本练习的意义(在实际使用中,您可能想使用标准库中的容器——虽然标准库确实提供了单向和双向——链表,它们很少有用)。
| 归档时间: |
|
| 查看次数: |
141 次 |
| 最近记录: |