不使用实例/引用计数器的Singleton对象是否应被视为C++中的内存泄漏?
如果没有计数器在计数为零时要求显式删除单例实例,该对象如何被删除?当应用程序终止时,操作系统是否清理它?如果那个Singleton在堆上分配了内存怎么办?
简而言之,我是否必须调用Singelton的析构函数,还是可以依赖它在应用程序终止时进行清理?
通常,"它取决于".在任何名副其实的操作系统中,当进程退出时,将释放进程内本地使用的所有内存和其他资源.你根本不需要担心.
但是,如果您的单例是在其自身进程外部使用生命周期分配资源(可能是文件,命名的互斥锁或类似的东西),那么您需要考虑适当的清理.
RAII将在这里为您提供帮助.如果你有这样的场景:
class Tempfile { Tempfile() {}; // creates a temporary file virtual ~Tempfile(); // close AND DELETE the temporary file }; Tempfile &singleton() { static Tempfile t; return t; }
...然后,您可以放心,您的临时文件将被关闭并删除,但您的应用程序将退出.但是,这不是线程安全的,并且对象删除的顺序可能不是您期望或要求的.
但是,如果你的单身人像这样实施
Tempfile &singleton() { static Tempfile *t = NULL; if (t == NULL) t = new Tempfile(); return *t; }
......那你有不同的情况.tempfile使用的内存将被回收,但不会删除该文件,因为不会调用析构函数.
您可以依赖操作系统清理它.
也就是说,如果你使用终结器而不是析构器处理垃圾收集语言,你可能希望有一个优雅的关闭程序,可以直接干净地关闭你的单例,这样他们可以释放任何关键资源,以防使用系统资源不能只需结束应用程序即可正确清理.这是因为终结器在大多数语言中都以"尽力而为"的方式运行.另一方面,很少有资源需要这种可靠性.无论如何,文件句柄,内存等都会干净利落地返回操作系统.
如果你使用一个懒惰分配的单例(即使用三重检查锁定成语)在c ++这样的语言中使用真正的析构函数而不是终结符,那么你就不能依赖于在程序关闭期间调用它的析构函数.如果您使用的是单个静态实例,则析构函数将在main完成某个时刻后运行.
无论如何,当进程结束时,所有内存都返回到操作系统.
您应该明确清理所有对象.永远不要依赖操作系统为您清理.
我通常使用单例来封装对文件,硬件资源等的控制.如果我没有正确清理该连接 - 我很容易泄漏系统资源.下次运行应用程序时,如果资源仍然被上一个操作锁定,则可能会失败.另一个问题可能是任何最终化 - 例如将缓冲区写入磁盘 - 如果它仍然存在于单例实例拥有的缓冲区中,则可能不会发生.
这不是内存泄漏问题 - 问题更多的是你可能正在泄漏资源而不是内存,这可能不容易恢复.
每种语言和环境都会有所不同,尽管我同意@Aaron Fisher的观点,即在整个过程中单身人士往往会存在.
在C++的例子中,使用典型的单例习语:
Singleton &get_singleton() { static Singleton singleton; return singleton; }
Singleton实例将在第一次调用函数时构造,并且同一实例将在程序关闭时在全局静态析构函数阶段调用析构函数.