C++ 不在程序结束前释放内存有多糟糕?

C++ 不在程序结束前释放内存有多糟糕?,c++,memory-management,singleton,C++,Memory Management,Singleton,作为一个示例,让我们讨论一下使用new(第一次调用getInstance()时创建实际实例的单例实现)方法,而不是使用静态字段。我突然意识到,它永远不会释放内存。但是,它必须在应用程序关闭之前立即释放内存,这样系统无论如何都会释放内存 除了糟糕的设计,这种方法还有什么实际的缺点 编辑:广告评论-所有有效的观点,谢谢各位。让我来问一下-对于一个单线程应用程序和POD单例类来说,有什么实际的缺点吗?理论上,我不会这么做 对于一个单线程应用程序和POD单实例类,有什么实际的缺点吗?理论上,我不打算这么

作为一个示例,让我们讨论一下使用
new
(第一次调用
getInstance()时创建实际实例的单例实现)
方法,而不是使用静态字段。我突然意识到,它永远不会释放内存。但是,它必须在应用程序关闭之前立即释放内存,这样系统无论如何都会释放内存

除了糟糕的设计,这种方法还有什么实际的缺点

编辑:广告评论-所有有效的观点,谢谢各位。让我来问一下-对于一个单线程应用程序和POD单例类来说,有什么实际的缺点吗?理论上,我不会这么做

对于一个单线程应用程序和POD单实例类,有什么实际的缺点吗?理论上,我不打算这么做

用标准语

[c++14 Object lifetime-4]对于具有非平凡析构函数的类类型的对象,在重用或释放该对象占用的存储之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数,或者如果使用删除表达式(5.3.5)如果不用于释放存储,则不应隐式调用析构函数,并且依赖于析构函数产生的副作用的任何程序都具有未定义的行为

在自动/静态变量等上隐式调用dtor


因此,(假设使用了一个新的表达式来构造对象),只要没有可观察的影响依赖于它的破坏(对于具有普通DTOR的类型,这一点非常正确),调用的分配函数的运行时实现就可以自由地释放内存并让对象在遗忘中衰减.

对所有单例类型使用schwartz计数器。
std::cout
就是这样实现的

好处:

  • 线程安全

  • 当单例相互依赖时,保证正确的初始化顺序

  • 在程序终止时更正销毁顺序

  • 不使用堆

  • 100%符合c++98、c++03、c++11、c++14、c++17

  • 不需要难看的getInstance()函数。只需使用全局对象

至于标题问题:

不在程序结束前释放内存有多糟糕

当程序在管理进程内存的操作系统下运行时,不释放内存并不坏


然而,单例所做的其他事情可能包括刷新IO缓冲区(例如,
std::cout
std::cerr
)。这可能是您希望避免丢失的东西。

这肯定是一个坏习惯。除此之外,它实际上取决于操作系统。大多数主要的PC操作系统在进程终止后会释放进程的所有资源。如果您在运行时使用泄漏检测器查找内存泄漏,而不是在进程终止前释放内存终止将给出一个假阳性。这取决于您拥有的新资源是否可能拥有外部资源,而不仅仅是内存,例如文件句柄等。您可能希望很好地关闭这些资源,而不是仅仅放弃它们。这就是为什么您应该始终对单例使用
静态
方法的原因:线程安全(以及其他一些方法)…我还建议您阅读(及其链接副本)。@NPS如果您使用
new
,您如何保证
getInstance
是无数据竞争的?当然可以使用互斥体,但是使用
静态
,您可以免费获得:)线程安全性如何@斯塔克:因为所有的单例都是以正确的顺序初始化的,所以任何依赖线程都会发现它已经被初始化了。好吧,但是属于不同TU的有序初始化序列可以同时发生,不是吗?。。。如果是,参考计数器访问仍应为synchronized@MassimilianoJanes不,它们是按顺序初始化的,但未指定TU之间的初始化顺序。schwartz计数器修复了这一问题,因为它为计数器使用未初始化的存储,标准要求在初始化静态对象之前,计数器为零填充。因此,在任何TU中创建schwartz计数器的inititaliser时,只有第一个如此初始化的计数器才会创建singleton。因此顺序总是正确的-因为任何使用单例的TU都会发现它已经初始化或将初始化它。我不是说初始化的相对顺序,我说的是在不同TU的动态初始化过程中对计数器的并发访问……这并没有说明不释放存储是否可以。@rustyx如果运行时在终止时释放内存,我的回答是不会发生ub;如果运行时没有,则所发生的是定义未定义的;)