我应该释放通常在程序结束时释放的长寿记忆吗?

时间:2015-08-30 03:07:16

标签: c++ memory-management

我目前正在编写一个将一些结构化二进制数据解析为一组对象的库。这些对象应该比任何用户代码都长,并且通常在主函数结束时或之后被释放。

我使用共享(和弱)指针来管理每个对象的内存,但它会给程序带来很多额外的复杂性,并引发我在这个特定问题中不会涉及的结构问题。

考虑到:

  • 遍历整个二进制数据是昂贵的,我不能多次这样做,
  • 每个访问过的条目用于构建一个对象,然后进行注册(即添加到集合中),
  • 二进制数据中的条目可能依赖于稍后出现但会立即解析的其他条目,并在再次访问该条目时进行注册,
  • 重复的条目可能随时出现,但我需要在注册之前将这些重复项合并到一个实例中(并将任何引用这些重复项的指针更新到新的合并条目),
  • 这些对象中的每一个都保证是派生公共类的众多POD类型之一,因此除了内存之外什么都不需要清理,
  • 生成的程序将在现代操作系统上运行(或者在这种情况下,从死进程中收集内存),

我很想使用原始指针,从不释放这些对象占用的内存,让操作系统在进程退出后进行清理。

什么是最好的行动方案?

8 个答案:

答案 0 :(得分:4)

如果您正在编写可重复使用的代码,则至少需要提供清理选项。如果某个程序使用您的库进行一次操作,然后继续运行怎么办?假设在您的图书馆任务完成后该过程立即退出是不安全的。

答案 1 :(得分:3)

其他答案涵盖了一般和标准方法:在一个理想的世界中,是的,你会清理你的记忆,因为它使代码更通用,更可重复使用并有助于工具。正如其他人所说,std::unique_ptr拥有指针和非拥有指针的原始指针应该可以正常工作。

有几种更专业的方法可能有用,也可能没用:

  • 使用池分配器(例如Boost.Pool或自己滚动)来预先分配一堆内存,然后为您的对象分配它们。然后,您可以通过删除池一次释放每个对象。
  • 故意不释放内存有时是一种有效的技术。参见,例如,"Increasing Compiler Performance by Over 75%",Walter Bright。当然,编译器是一个专门的问题域,而Walter Bright可能是最活跃的顶级编译器开发者之一,因此适用于他的问题域的技术不应盲目地应用于其他地方。

答案 2 :(得分:2)

  
      
  • 生成的程序将在现代操作系统上运行(或者在这种情况下,从死进程中收集内存)
  •   
     

我很想使用原始指针,从不释放这些对象占用的内存,让操作系统在进程退出后进行清理。

如果您采用这种方法,那么任何使用您的库然后使用valgrind尝试检测程序中的内存泄漏的人都会报告来自您的库的大量泄漏并向您抱怨它,所以如果我是你,我绝对不会这样做。

答案 3 :(得分:1)

如果你正在编写一个库,那么你应该提供一个清理函数来释放你分配的所有内存。

为什么这有用的一个实际示例是Windows DLL使用您的库。加载库时,将初始化静态数据。卸载库时,将清除静态数据。如果你的库有一些永远不会被释放的内存的全局指针,那么DLL的加载 - 卸载周期将泄漏内存。

答案 4 :(得分:1)

如果您的所有权和生命周期都很明确,我建议您使用shared_ptr作为非拥有指针的拥有指针和原始指针。它应该比weak_ptr=E2&CHAR(10)&CHAR(10)&"Here are your tip numbers:"&CHAR(10)&CHAR(10)&"Cash Tips: "&I2&" Credit Card Tips: "&J2&" Form 4070's are due in the office by 4pm Wednesday and they are required by law from every tipped employee. Thank you. -- Management Team" 复杂,同时仍然自动管理内存。

我认为根本不管理内存是一种选择。但我认为使用智能指针来表达所有权不仅仅是关于良好的内存管理,它还使代码更易于推理。

答案 5 :(得分:1)

如果对象都是相同的类型,那么您可以将它们全部放入vector并将它们全部引用到索引号而不是使用指针,而不是单独分配每个对象。向量的内置内存管理负责根据需要分配空间,当你完成对象时,你可以破坏向量来一次解除所有对象。 (请注意,vector::clear()实际上并没有释放内存,但它确实可以在向量中存储一组新对象。)

如果您的对象不是同一类型,那么您将要查看region-based memory management的更一般概念。如上所述,我们的想法是你可以将所有对象分配到相对较少的内存块(可能只有一个)中,以后可以释放它而无需跟踪所有内存。 在其中分配的单个对象。

答案 6 :(得分:0)

尝试考虑未来的维护工作。假设您的代码需要分解或其他内容完成后再进行。在这种情况下,你可能会在以后泄露自己或者成为资源困境。

答案 7 :(得分:0)

清理(或能够做到)是好的。现在看起来很明显应用程序应该在整个生命周期中使用单个结构化二进制数据集,但是一旦您意识到需要编写需要重置一半并重新开始的应用程序,您就会开始自己开始与另一个数据集。

(一个容易忽视的相关问题是,应用程序可能需要同时使用两个完全独立的数据集,因此尽量不要设计您的库以排除该用例!)

那就是说,我认为你可能过于关注极端。不应该参与内存管理的代码可以使用原始指针,如果没有这些指针在内存中超过结构化数据集的风险,这是合理的。

然而,这并不意味着 参与内存管理的代码也需要使用原始指针。即使您将原始指针传递给用户,也可以使用智能指针来管理数据结构。

除此之外,请记住,根据我的经验,指针通常是错误的语义 - 通常,大多数用例是最自然的 reference value 语义,这意味着您应该传递原始引用,或者传递具有引用或值语义的轻量级包装类,但实现为包含指向实际数据的指针。或者甚至作为实际数据的副本(如果适用)。