【问题标题】:C++ freeing static variablesC++ 释放静态变量
【发布时间】:2011-01-26 15:06:29
【问题描述】:

我希望我的类有一个指向动态分配的内存区域的静态指针。我了解如何初始化它 - 在我的情况下,我将在第一个对象需要它时对其进行初始化。但是,我不知道代码中何时/何地释放它。我想在程序终止时释放它。

我可能能够释放对象析构函数中的指针,但是我必须维护一个对象计数,以查看当对象是最后一个正在使用的对象时释放是否安全。

有没有更优雅的方法来做到这一点?

请告诉我。

谢谢, jbu

【问题讨论】:

  • 听起来您正在尝试重新发明智能指针?
  • 此问题是实现单例 (en.wikipedia.org/wiki/Singleton_pattern) 时出现的问题之一。 @Klaim 下面的代码示例就是其中的一个示例。它们可以是一件好事或拐杖。不要滥用它们。 :)

标签: c++ memory static free destructor


【解决方案1】:

这里有两种解决方案:

  1. 不要删除删除它(你在 C++ 中,你使用 new 和 delete,对吗?;))。今天几乎所有的操作系​​统都会在应用程序完成后“释放”应用程序分配的内存。但这不是一个好的解决方案,例如,它会使内存泄漏难以检测。
  2. 将你的指针封装到一个类中(作为成员),然后使用这个类作为你的静态类型。这样,您就知道类析构函数将在应用程序结束时被调用。然后,您只需删除析构函数中的数据,即可完成并清理工作。这就是 RAII 的力量。

我建议你做 2,这是一种非常干净的方法。


这是一个简单的例子。而不是这样做

static Thing* things = new Thing(); // or whatever way to initialize, here or in a specific function

你会这样做的:

class ThingManager // or whatever name you like
{
public:
   ThingManager( Thing* thing ) : m_thing( thing ) { }//or create it here? whatever solution suits your way of creating the data

   ~ThingManager() { delete m_thing; } // THAT's the important part!

    Thing* instance() const { return m_thing; } // or whatever accessor you need, if you need one

private:
    Thing* m_thing;
};

然后

static ManagedThing thing; // now i can access it via thing.instance() 

当程序结束时,静态变量(不再是指针)将被销毁并调用它的析构函数。

编写它只是为了让您了解如何做到这一点。

【讨论】:

  • 对不起,我对 C++ 很陌生。当您说“将在应用程序结束时调用类析构函数”时,您是说有一个对象析构函数和一个静态类析构函数吗?另外,什么是 RAII?
  • RAII : en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization 你应该了解所有类的默认功能。
  • RAII 是一个不幸的术语,因为它不是很具描述性;见en.wikipedia.org/wiki/RAII
  • +1 我们真的应该开始使用另一个术语,这将有助于为新手采用范式。
【解决方案2】:

将其放入智能指针中。它将具有静态生命周期,并在main 返回后被销毁:

static std::auto_ptr<T> thePointer;

另一种选择是注册你自己的atexit函数:

// static
void YourClass::freePointer(void)
{
    delete getPointer();
}

// static
T* YourClass::getPointer(void)
{
    if (!thePointer)
    {
        thePointer = new T;
        atexit(freePointer);
    }

    return thePointer;
}

这将产生相同的效果。您已经提到的另一个选择是保留一个静态计数器。请注意,您实际上可以非常有效地完成它。

【讨论】:

  • Alexandrescu 在他的Loki::Singleton 的实现中使用了atexit。这样,他确保释放指针的方法也将其状态重置为0,以便在必要时可以重新分配(Phoenix 生命周期策略的情况)。我个人认为auto_ptr 甚至更好的unique_ptr 麻烦少得多。
  • 这样不好,at_exit清理函数的数量有限,有时低至32(标准中定义的最小值)
【解决方案3】:

从操作系统的角度来看,当您的程序终止时释放内存并没有真正意义,所做的只是减慢终止速度。终止您的应用程序会破坏您的整个地址空间,它会立即释放您在堆上分配的所有内容。在应用程序关闭时显式调用free 只是在堆中洗牌,无论如何都会被丢弃。

我们如此努力地明确释放所有内容的主要原因是确保我们不会泄漏内存并且我们的内存占用不会永远增长。

但是,如果您可以确定这是静态的,并且只有一个,并且在所有其他对象都被释放之前您不能安全地释放它,那么这种情况可能会更好让应用程序终止为您处理。

【讨论】:

  • 感谢您的回复。我一直认为即使在程序终止后,内存泄漏也会使操作系统保留保留/未释放的内存。
  • 虽然用一些内存泄漏检测工具会掩盖其他内存泄漏,所以还是不推荐。
  • 在关机前释放内存还有一个好处——它使真正的内存泄漏更容易被发现。如果您使用工具来跟踪分配,那么您不删除的任何内容都将成为您必须涉足的噪音。
  • @John:这个论点只适用于具有虚拟内存管理器的操作系统。例如,一些实时/嵌入式操作系统没有这样的管理器。在这些平台上,终止时泄漏肯定是一个问题。 IMO,依靠操作系统 VMM 进行清理是一个坏习惯。
  • @Void:从技术上讲,它不依赖于具有 VMM 的操作系统。我使用了一个没有虚拟内存的系统,但是 mallocfree 将块添加/删除到分配列表中。每个分配花费 8 个字节和一个中断关闭/开启周期,但允许操作系统在进程终止时释放它们。每进程堆可以实现相同的目的(我认为这个系统不使用堆的原因是因为它们使得将释放的内存释放回操作系统以供其他进程使用变得更加困难,这在这个系统上是可取的)。
【解决方案4】:

您可以将静态变量声明为智能指针,然后当程序完成时,分配的指针将被释放。

【讨论】:

    【解决方案5】:

    我会在类中定义一个静态计数器来跟踪对象实例计数 当析构函数执行时,它会减少计数器,如果 counter== 0 也释放内存..就像你一样

    【讨论】:

      【解决方案6】:

      当我试图让我自己的静态指针在退出时被释放时,我刚刚看到了这篇旧帖子。

      正确的 C++ (C++11) 解决方案是使用智能指针,或者 std::unique_ptr&lt;T&gt;std::shared_ptr&lt;T&gt; 其中 T 是您正在初始化的对象的类型。这些模板分别管理对象的生命周期,并在对象超出范围或成为内存中的最后一个引用时将其删除。

      【讨论】:

        猜你喜欢
        • 2011-07-30
        • 1970-01-01
        • 1970-01-01
        • 2011-11-11
        • 2011-12-18
        • 2013-02-09
        • 1970-01-01
        • 1970-01-01
        • 2018-11-14
        相关资源
        最近更新 更多