为啥函数执行后没有释放堆上的元素?
Posted
技术标签:
【中文标题】为啥函数执行后没有释放堆上的元素?【英文标题】:Why aren't elements on the heap released after the function?为什么函数执行后没有释放堆上的元素? 【发布时间】:2020-07-22 20:24:36 【问题描述】:有人告诉我'是的。 node* new_node = new node;
在堆上分配一个节点,而函数内部的node new_node;
在堆栈上分配一个节点。如果节点相互指向,它仍然是一个链表。请注意,当函数结束时,堆栈分配的东西会自动释放。这就是为什么在堆上分配更方便。'
这是什么意思?有人可以详细说明吗?
【问题讨论】:
这可能有助于理解堆栈和堆之间的区别...gribblelab.org/CBootCamp/7_Memory_Stack_vs_Heap.html 【参考方案1】:很长的答案。
自动存储时长
“堆栈”变量(更恰当地称为具有自动存储持续时间的实体)在您离开声明它们的范围后立即被销毁。 (即他们被“清理”并释放他们的内存)
void my_function()
node node1;
if (1 == 1)
node node2;
node* node3_ptr = new node; // this node is *not* cleaned up automatically
// node2 is destructed now
node node4;
// node1 and node4 are destructed now
在上面的代码中,node1
和 node4
在函数最外层范围的不同部分声明。当函数结束时,它们会“消失”。
函数是否运行到最后、提前返回、抛出异常都没有关系——如果函数结束,C++ 保证它们会被销毁。 (终止在其轨道上的应用程序是不同的。)
node2
在 if
块内声明。当代码离开if
块时,它将被销毁——甚至在函数结束之前。
保证在完全可预测的时间自动销毁这些变量是 C++ 的最大优势之一。它被称为“确定性破坏”,这也是 C++ 是我首选语言的原因之一。
动态存储时长
“堆”变量(也就是具有“动态”存储位置的实体)比较棘手。
void my_leaky_function()
node* node5;
new node;
node* node6 = new node;
node5
仍然只是一个局部变量。它的类型是“指向节点的指针”而不仅仅是“节点”这一事实并不重要。它是一个具有自动持续时间的变量,它使用内存。它的大小是指针的大小(可能是 4 或 8 个字节 - 取决于您的平台),而不是节点的大小。该变量“消失”并在函数结束时恢复其内存。
new node;
在“空闲存储”(俗称“堆”)上分配内存。 new
返回一个指向已分配内存的指针,但此代码忽略了该指针。这里不涉及局部变量,函数结束时节点不被销毁;
node* node6 = new node;
也为空闲存储上的节点对象分配了足够的空间——但这次new
返回的指针存储在名为node6
的局部变量中。注意:node6
是一个局部变量(存储指针,而不是节点),它具有自动存储持续时间。当函数结束时,node6
变量消失(并且它使用的几个字节内存被释放)。但是node6
也指向的节点 - 存储在免费存储中的节点 - 没有被破坏。
当这个函数结束时,它在空闲存储中留下了两个节点 - 因为它已经丢弃了指向每个节点的指针,所以任何人都无法删除它们。它有“内存泄露”。
为什么要使用动态存储?
C++ 承诺在您离开函数作用域时清理函数的自动存储值。这通常是您想要的。
有时一个函数需要创建一个比函数调用更有效的值 - 一个在函数退出时不能被销毁的值。
通常该值可以返回给调用者(没有new
,没有指针),调用者可以用它做他们想做的事。通常,该值存储在某个集合中,例如已经为其分配内存的向量或数组。但是 - 在您的示例中,您想在链表或树(或类似的东西)中创建一个新节点,并且在函数结束时销毁该节点是没有意义的。
tl;博士;
所以
-
如果一个值必须在创建它的函数结束后存在
它不只是返回给函数的调用者
并且它没有存储在其他容器的内存中
那么免费商店就是适合它的地方。
关于谁“拥有”该值并负责删除它 - 以及使用智能指针而不是原始指针 - 以及异常安全性 - 和和和 - 但这个答案已经是比我想要的大。所以让我以这个结束:
在学习 C++ 时,请使用指针。使用免费商店。用内存泄漏和双重删除烧伤自己。弄清楚你将如何解决这些问题。这为您理解稍后将使用的抽象奠定了良好的基础。
一旦你了解了指针和动态存储——一旦你了解了如何从头开始编写你自己的链表或二叉树——停止使用它们。在日常编码中,除非您是为容器库编写代码的专家,否则永远不要再使用 new
或 delete
。绝对必要时使用智能指针,但尽量避免使用它们。
尽可能依靠自动存储期限。确定性破坏是你的朋友。这就是 C++ 与 C 的不同之处。这就是 C++ 与垃圾收集语言的不同之处。这就是为什么 C++ 仍然是编程语言之王之一。
【讨论】:
【参考方案2】:您仍然可以在之前的方法调用中让指针引用相同的对象。
【讨论】:
以上是关于为啥函数执行后没有释放堆上的元素?的主要内容,如果未能解决你的问题,请参考以下文章