【问题标题】:How to check if memory has aready been released in Destructor?如何检查内存是否已经在析构函数中释放?
【发布时间】:2010-12-12 07:39:22
【问题描述】:

我有一个使用 allegro 开源库的简单坦克战争风格游戏。在我的坦克类中,我将指向位图对象的指针数组初始化为 0。然后我使用 allegro 函数 create_bitmap 创建新对象,该函数分配内存并对其进行初始化。

然后我照常工作。

问题是,当我像一个优秀的 OO 男孩一样去释放类析构函数中的位图内存时,我使程序崩溃了,因为在这个特定的程序中,allegro 库进行了它的清理(它释放了它创建的位图对象)在类超出范围并被销毁之前。它不会再次将我的指针设置为 NULL,所以我无法检查位图是否仍然有效,如果我尝试释放它们,它们会使程序崩溃。

有没有办法解决这个问题?如果它们不为 NULL,我可以检查有效的指针吗?如果在程序中以不同的方式使用该类,我如何确定已释放内存。就目前而言,我基本上是在调用 new 而不删除,我不喜欢它。

【问题讨论】:

  • “顺便说一句,Allegro 给出了删除资源的 destroy_bitmap 命令” - 那么为什么不在你的 dtor 中调用 destroy_bitmap 呢?
  • 我一直在尝试编辑 OP 说我知道。这就是我在 dtor 中使用的。问题是,如果 allegro 已经释放了位图,当我将释放的指针传递给 destroy_bitmap 命令时,它会因未分配内存而崩溃并出现非法访问错误,并且 allegro 显然没有跟踪这一点。
  • alleg.sourceforge.net/stabledocs/en/alleg009.html#create_bitmap 上的 allgro 文档表明,使用 create_bitmap 分配的内存是您释放(使用 destroy_bitmap)的责任。
  • @Bill 我正试图释放它,但 allegro 不让我这样做,因为它认为我忘记了并自己做。

标签: c++ memory-leaks destructor allegro


【解决方案1】:

我想说您应该确保在关闭 Allegro 之前销毁所有对象,您可以通过(如果它们在堆栈上)在关闭 Allegro 之前关闭它们存在的范围来轻松做到这一点。

如果您需要在此之前关闭 Allegro(例如,由于致命错误),那么您可以调用 exit,在这种情况下不会运行析构函数(但您的程序仍然不会崩溃)。

不要花太多时间确保程序在退出时清理干净,节省您的精力以确保它在运行时不会泄漏:)

【讨论】:

    【解决方案2】:

    我认为问题不在于 allegro 释放位图本身(否则您不需要在退出时释放它们)而是 allegro 库在调用析构函数之前已被取消初始化。

    int main()
    {
        ObjectManagingBitmaps o;
        ...
        return 0;
        //allegro automatically shut down here
    } //o destructor invoked here
    END_OF_MAIN()
    

    为了确保首先调用析构函数,您可以使用人工作用域:

    int main()
    {
        {
        ObjectManagingBitmaps o;
        ...
        } //o destructor invoked here
        return 0;
        //allegro automatically shut down here
    } 
    END_OF_MAIN()
    

    【讨论】:

    • 他的对象是不是主要的?我敢打赌他的对象是全球性的。
    • 那太不友好了。在这种情况下,全局可以是一个在主结束之前被删除的指针,或者您可以提供一个释放方法(并且可能在析构函数中检查 NULL)。或者......如果该对象打算用作单个全局/单例,则让操作系统清理分配。
    • 我的对象是 main 的本地对象。在返回之前,allegro 需要调用 allegro_exit() 来取消初始化库。然后当 main 返回时对象超出范围。你说的都是准确的。
    【解决方案3】:

    在处理指向堆内存的多个指针时使用引用计数。基本上,如果您对同一内存有多个引用并删除一个,则另一个引用可能仍然认为它存在。

    http://en.wikipedia.org/wiki/Reference_counting

    【讨论】:

      【解决方案4】:

      什么时候调用析构函数?是在 Allegro 库关闭之后吗?如果是这样,你可以先删除所有对象吗?

      【讨论】:

        【解决方案5】:

        您确定您使用正确吗?我挖出了一些旧的 Allegro 代码,我有一个带有 create_bitmap 调用的构造函数和一个带有 release_bitmap 调用的析构函数,它工作正常。

        我不记得 Allegro 自动为你释放内存的任何事情。您是否不小心用一些非内存值覆盖了指针?是否还有其他地方可以释放此指针?

        【讨论】:

        • 有,我在 OP 中添加了很多。 destroy_bitmap(BITMAP*) 是我试图在 dtor 中使用的。此外,正如我之前所说,Allegro 尝试在 dtor 甚至被调用之前自动执行此操作,这是我问题的根源。
        • 更正,我试图将它添加到 OP,所以由于某种原因没有响应我的编辑。
        • 您确定 Allegro 正在释放内存吗?您是否不小心覆盖了指针或在其他地方释放了位图?
        • @Matt B,没有重新分配位图的地方。在调用 Allegro_exit() 之前它们很好,在它们消失之后。
        【解决方案6】:

        您不应该使用原始指针数组。 Allegro 带有create_bitmap 和destroy_bitmap 函数。这与构造函数和析构函数的 C++ 概念非常吻合。你应该有一个AllegroPlusPlus::bitmap 类,它只管理一个位图。您的 Tank 类可以简单地拥有一组这些。

        这是职责分离。 tank 类不应该对位图及其内存管理了解太多,而 bitmap 类应该只处理一个位图。

        您想在 Tank 类中回收位图。这没问题;它可以通过良好的bitmap::operator=(bitmap const&) 或其他重载实现轻松完成。但同样,让该分配成为位图类的职责,而不是坦克类。

        【讨论】:

        • SO 在我尝试编辑帖子时没有响应,所以我将其放在这里。这是功课,在指示参数之外添加类是不好的。
        • 不允许在 C++ 类中添加类?怎么回事?如果您不能将新课程作为作业的一部分,您如何让 dtor 遇到问题?
        • @jmucchiello 我有一个 dtor 问题的类,我被指示创建。我在之前的作业中被嘲笑,因为我使用的主题对于我所在的班级来说太高级了。
        【解决方案7】:

        听起来很可怕的leaky abstraction

        如果你不知道它是如何被分配的,你就不能指望找到一种安全的方法来销毁内存。清理功能听起来是有原因的,而且在做一项工作 - 只是要忍受它。

        您当然可以包装这些位并在 cmets 中包含一些文档,这样其他开发人员就不会落入同样的陷阱。

        同时分析您的应用程序以确保没有泄漏。

        【讨论】:

          【解决方案8】:

          显式管理内存的关键是,虽然您可以有多个指向同一内存区域的指针,但在任何时候,只有一个是指定的所有者,而所有其他人只共享它。当堆对象拥有其他堆对象时,它们构成一棵树,根植于某个全局或局部范围的变量中。

          一旦您从初始 Allegro 调用返回,您应该将 Allegro 视为您传递给它的内存区域的所有者,并且您自己的指针只是一个共享指针。

          不,除了一些分配器的诡计,你没有标准的方法来确定内存是否有效。分配器技巧可用于调试目的,但不要弄乱库的内部结构。

          【讨论】:

          • 如果我在 dtor 中将所有指针设置为 NULL 会怎样?那么我已经完成了我的工作,如果他们没有被清除,它就在 allegro 上?
          • 是的,至少在调试代码中这也是一种很好的做法,可以通过指向持有者的无效指针及早触发错误。
          • 这本身还不错,但是如果您的程序正确,它会浪费一两个 CPU 周期...这取决于您对防御 (en.wikipedia.org/wiki/Defensive_programming) 与快速失败 ( en.wikipedia.org/wiki/Fail-fast) 编程。在调试模式下,我倾向于快速失败;在生产代码中,我倾向于支持容错(如果系统现在可以容忍,则取决于系统以后会搞砸的程度)。
          【解决方案9】:

          如果指针不为 NULL,我可以检查有效指针吗?

          没有。但在你的情况下,你不需要。由于 Allegro 承诺会处理其资源,因此您不必(也不得)干预 Allegro 资源的资源处理。特别是,由于您甚至不知道资源是如何分配的,因此您无法取消分配它们。

          【讨论】:

          • Allegro 承诺在程序的最后执行此操作,但如果我在游戏中多次重复使用和删除此坦克对象,则每次位图都不会被正确释放。顺便说一句,Allegro 提供了 destroy_bitmap 命令来删除资源。
          • 这似乎表明您的对象是静态分配的。由于未指定静态对象的构造(和销毁!)顺序,因此请努力防止这种情况发生。如果您的对象不是静态的,则不应在范围结束时删除 Allegro 资源,一切都很好。那我看不到问题所在。
          猜你喜欢
          • 2022-08-16
          • 2010-10-09
          • 2016-10-25
          • 1970-01-01
          • 2017-02-16
          • 2013-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多