C++释放静态变量

28

我希望我的类拥有一个指向动态分配内存区域的静态指针。我知道如何初始化它 - 在我的情况下,当第一个对象需要它时,我会初始化它。然而,我不知道在什么时候/什么地方释放它。我希望程序终止时可以释放它。

也许我可以在我的对象析构函数中释放指针,但是那样的话我就必须维护一个对象计数器来确定在对象是最后一个正在使用的对象时是否安全释放。

有没有更优雅的方式来做这个?

请告诉我。

谢谢, jbu


1
听起来你正在尝试重新发明智能指针? - Martin Beckett
这个问题是在实现单例模式时出现的问题之一(http://en.wikipedia.org/wiki/Singleton_pattern)。@Klaim的代码示例是其中之一。它们可以是好事,也可以是紧急情况。不要滥用它们。 :) - Void
6个回答

23
你有两个解决方案:
  1. 不要删除它 (你正在使用 C++,你用 new 和 delete,对吧?;))。几乎所有现代操作系统都会在应用程序完成后“释放”内存分配,但这并不是一种好的解决方案,例如,这会使内存泄漏难以检测。
  2. 将指针封装到类中(作为成员),然后使用该类作为静态类型。这样,您就可以确保类析构函数会在应用程序结束时被调用。然后您只需在析构函数中删除数据,工作就完成了并且干净利落。这就是 RAII 的威力。

我建议你选择第二种方法,这是一种非常干净的方法。


下面是一个简单的示例。不要这样做:

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是什么? - jbu
RAII:http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization 你应该了解所有类的默认函数。 - Klaim
4
RAII是一个不太具描述性的术语,这很不幸。请参见http://en.wikipedia.org/wiki/RAII。 - Mark Ransom
+1 我们真的应该开始使用另一个术语,这将有助于新手采用这种范式。 - Klaim

17

将它放入智能指针中。它将具有静态生命周期,并在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;
}

这会产生相同的效果。你已经提到的另一个选项是保持静态计数器。请注意,你实际上可以将其有效地封装起来。


1
Alexandrescu在他的Loki::Singleton实现中使用了atexit。这样,他确保释放指针的方法也将其状态重置为0,以便在需要时可以重新分配(例如Phoenix生命周期策略的情况)。个人认为,使用auto_ptr或者更好的unique_ptr会更加方便。 - Matthieu M.
这不太好,只有一小部分有限的 at_exit 清理函数可用,有时甚至只有 32 个(标准中定义的最小值)。 - Lothar

7
从操作系统的角度来看,在程序终止时释放内存并没有实际意义,这只会拖慢终止速度。当你的应用程序终止时,它会撤销整个地址空间,这将一次性释放你在堆上分配的所有内容。在应用程序关闭时显式调用free只是在堆中移动指针,而这些指针最终都会被抛弃。
我们努力显式释放所有东西的主要原因是确保我们没有泄漏内存,我们的内存占用量不会无限增长。
但是,如果你可以确定这是静态的,只会有一个,并且在所有其他对象被释放之前不能安全地释放它,这种情况下最好让应用程序终止为你处理它。

2
虽然这样做可以掩盖其他内存泄漏,但在任何情况下都不建议这样做。 - Klaim
2
在关机前释放内存仍然有好处 - 它使得真正的内存泄漏更容易被发现。如果您使用工具来跟踪分配情况,那么您没有删除的任何内容都将成为您必须浏览的噪音。 - Mark Ransom
1
@John:这个论点只适用于具有虚拟内存管理器的操作系统。例如,一些实时/嵌入式操作系统就没有这样的管理器。在这些平台上,在终止时泄漏肯定会成为一个问题。依赖操作系统的VMM来进行清理是一个不好的习惯,我认为应该避免。 - Void
@Void:它在技术上不依赖于操作系统具有虚拟内存管理器(VMM)。我曾经使用过一个没有虚拟内存的系统,但是其中的 mallocfree 将分配块添加/删除到分配列表中。每个分配需要 8 个字节和一次中断开/关循环的成本,但允许操作系统在进程终止时释放它们。每个进程的堆可以实现相同的功能(我认为该系统没有使用堆的原因是因为它们使释放空闲内存以供其他进程使用变得更加困难,而这对该系统是可取的)。 - Steve Jessop
@Void:这是一件相当少见的事情,一个嵌入式操作系统,其中大多数低级代码都是用自定义汇编语言编写的。用C语言编写的应用程序被认为是“高级”的,如果它们愿意承担自己的内存管理责任,那么低级分配器就对它们可用。这就是为什么在malloc/free上施加开销是可以接受的原因。 - Steve Jessop
显示剩余2条评论

2

您可以将静态变量声明为智能指针,这样当程序结束时,分配的指针将被释放。


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

0

当我试图在退出时释放自己的静态指针时,我偶然发现了这篇旧文章。

正确的C++(C++11)解决方案是使用智能指针,可以使用std::unique_ptr<T>std::shared_ptr<T>,其中T是您正在初始化的对象类型。这些模板管理对象的生命周期,并在作用域结束时或成为内存中最后一个引用时删除对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接