C++ 单例析构函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/273720/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:13:40  来源:igfitidea点击:

Singleton Destructors

c++memory-managementsingleton

提问by Bryan Marble

Should Singleton objects that don't use instance/reference counters be considered memory leaks in C++?

不使用实例/引用计数器的单例对象是否应该被视为 C++ 中的内存泄漏?

Without a counter that calls for explicit deletion of the singleton instance when the count is zero, how does the object get deleted? Is it cleaned up by the OS when the application is terminated? What if that Singleton had allocated memory on the heap?

如果没有计数器在计数为零时要求显式删除单例实例,对象如何被删除?当应用程序终止时,它是否由操作系统清除?如果单例在堆上分配了内存怎么办?

In a nutshell, do I have to call a Singelton's destructor or can I rely on it getting cleaned up when the application terminates?

简而言之,我是否必须调用 Singelton 的析构函数,还是可以依赖它在应用程序终止时被清理?

采纳答案by Edward KMETT

You can rely on it being cleaned up by the operating system.

您可以依赖它被操作系统清理。

That said, if you are in a garbage collected language with finalizers rather than destructors you may want to have a graceful shutdown procedure that can cleanly shutdown your singletons directly so they can free any critical resources in case there are using system resources that won't be correctly cleaned up by merely ending the application. This is because finalizers run on a sort of 'best effort' basis in most languages. On the other hand there a very very few resources that need this sort of reliability. file handles, memory, etc. all go back to the OS cleanly regardless.

也就是说,如果您使用带有终结器而不是析构函数的垃圾收集语言,您可能希望有一个优雅的关闭过程,可以直接干净地关闭您的单例,以便它们可以释放任何关键资源,以防使用系统资源不会只需结束应用程序即可正确清理。这是因为终结器在大多数语言中都以“尽力而为”的方式运行。另一方面,很少有资源需要这种可靠性。无论如何,文件句柄、内存等都会干净地返回到操作系统。

If you are using a singleton that is lazily allocated (i.e. with a triple-check lock idiom) in a language like c++ with real destructors rather than finalizers, then you cannot rely on its destructor being invoked during program shutdown. If you are using a single static instance then the destructor will run after main completes at some point.

如果您在像 c++ 这样的语言中使用延迟分配的单例(即使用三重检查锁习惯用法),带有真正的析构函数而不是终结器,那么您不能依赖于在程序关闭期间调用它的析构函数。如果您使用的是单个静态实例,那么析构函数将在 main 在某个时刻完成后运行。

Regardless, when the process ends, all memory returns to the operating system.

无论如何,当进程结束时,所有内存都返回给操作系统。

回答by Roddy

As so often, "it depends". In any operating system worthy of the name, when your process exits, all memory and other resources used locally within the process WILL be released. You simply don't need to worry about that.

像往常一样,“这取决于”。在任何名副其实的操作系统中,当您的进程退出时,进程内本地使用的所有内存和其他资源都将被释放。你根本不需要担心这个。

However, if your singleton is allocating resources with a lifetime outside it's own process (maybe a file, a named mutex, or something similar) then you do need to consider the appropriate cleanup.

但是,如果您的单例在其自己的进程(可能是文件、命名的互斥锁或类似的东西)之外分配具有生命周期的资源,那么您确实需要考虑适当的清理。

RAII will help you here. If you have a scenario like this:

RAII 将在这里为您提供帮助。如果你有这样的场景:

class Tempfile
{
Tempfile() {}; // creates a temporary file 
virtual ~Tempfile(); // close AND DELETE the temporary file 
};

Tempfile &singleton()
{
  static Tempfile t;
  return t;
}

...then you can be reassured that your temporary file WILL be closed and deleted however your application exits. However, this is NOT thread-safe, and the order of object deletion may not be what you expect or require.

...那么您可以放心,无论您的应用程序退出,您的临时文件都将被关闭和删除。但是,这不是线程安全的,并且对象删除的顺序可能不是您期望或要求的。

however, if your singleton is implemented like THIS

但是,如果你的单例是这样实现的

Tempfile &singleton()
{
  static Tempfile *t = NULL;
  if (t == NULL)
    t = new Tempfile(); 
  return *t;
}

... then you have a different situation. The memory used by your tempfile will be reclaimed, but the file will NOT be deleted because the destructor will not be invoked.

……那你的情况就不一样了。临时文件使用的内存将被回收,但文件不会被删除,因为不会调用析构函数。

回答by Klathzazt

You should explicitly clean up all your objects. Never rely on the OS to clean up for you.

您应该明确清理所有对象。永远不要依赖操作系统为您清理。

Where I usually use a singleton is to encapsulate control of something like a file, hardware resource, etc. If I don't properly clean up that connection- I can easily leak system resources. The next time the application runs, it might fail if the resource is still locked by the previous operation. Another issue could be that any finalization- such as writing a buffer to disk- might not occur if it still exists in a buffer owned by a singleton instance.

我通常使用单例的地方是封装对文件、硬件资源等的控制。如果我没有正确清理那个连接 - 我很容易泄漏系统资源。应用程序下次运行时,如果资源仍被前一个操作锁定,它可能会失败。另一个问题可能是,如果它仍然存在于单例实例拥有的缓冲区中,则任何终结(例如将缓冲区写入磁盘)都可能不会发生。

This is not a memory leaking issue- the issue is more that you may be leaking resources like other than memory which may not be as easily recovered.

这不是内存泄漏问题 - 问题更多的是您可能会泄漏诸如内存以外的资源,而这些资源可能不容易恢复。

回答by Don Wakefield

Each language and environment will differ, though I agree with @Aaron Fisher that a singleton tends to exist for the duration of the process.

每种语言和环境都会有所不同,尽管我同意 @Aaron Fisher 的观点,即在整个过程中往往会存在单例。

In the example of C++, using a typical singleton idiom:

在 C++ 的例子中,使用典型的单例习惯用法:

Singleton &get_singleton()
{
   static Singleton singleton;
   return singleton;
}

the Singleton instance will be constructed the first time the function is called, and the same instance will have it's destructor called during the global static destructor phase at program shutdown.

Singleton 实例将在第一次调用函数时被构造,并且在程序关闭时的全局静态析构函数阶段,同一实例将调用它的析构函数。

回答by Nicola Bonelli

Any kind of allocation, except those in shared memories, are automatically cleaned up by the operating system when the process terminates. Therefore you should not have to explicitly call the singleton destructor. In other words no leaks...

当进程终止时,操作系统会自动清除除共享内存中的分配之外的任何类型的分配。因此,您不必显式调用单例析构函数。换句话说,没有泄漏......

Furthermore a typical singleton implementation like the Meyers' Singleton is not only thread safe during the initialization on the first call but also guaranteed to graceful terminate when the application exits (the destructor is invoked).

此外,像 Meyers 的 Singleton 这样的典型单例实现不仅在第一次调用的初始化期间是线程安全的,而且还保证在应用程序退出(调用析构函数)时正常终止。

Anyway if the application is sent a unix signal (ie: SIGTERMor SIGHUP) the default behavior is to terminate the process without calling the destructors of static allocated objects (singletons). To overcome this issue for these signals it is possible to dispose a handler calling exit, or dispose exit be such handler -- signal(SIGTERM,exit);

无论如何,如果向应用程序发送了一个 unix 信号(即:SIGTERMSIGHUP),默认行为是终止进程而不调用静态分配对象(单例)的析构函数。为了克服这些信号的这个问题,可以处理调用 exit 的处理程序,或者处理 exit 是这样的处理程序——signal(SIGTERM,exit);

回答by David Norman

How are you creating the object?

你是如何创建对象的?

If you're using a global variable or static variable, the destructor will be called, assuming the program exits normally.

如果您使用的是全局变量或静态变量,则假定程序正常退出,析构函数将被调用。

For example, the program

例如,程序

#include <iostream>

class Test
{
    const char *msg;

public:

    Test(const char *msg)
    : msg(msg)
    {}

    ~Test()
    {
        std::cout << "In destructor: " << msg << std::endl;
    }
};

Test globalTest("GlobalTest");

int main(int, char *argv[])
{
    static Test staticTest("StaticTest");

    return 0;
}

Prints out

打印出来

In destructor: StaticTest 
In destructor: GlobalTest

回答by Thorsten79

It's folklore to free global memory allocations explicitly before the application terminates. I suppose most of us do it out of habit and because we feel it's sort of bad to "forget" about a structure. In the C world it's a law of symmetry that any allocation must have a deallocation somewhere. C++ programmers think differently if they know and practice RAII.

在应用程序终止之前显式释放全局内存分配是民间传说。我想我们大多数人都是出于习惯而这样做的,因为我们觉得“忘记”一个结构有点不好。在 C 世界中,任何分配都必须在某处解除分配,这是对称定律。如果 C++ 程序员了解并实践 RAII,他们的想法就会不同。

In the good old days of e.g. AmigaOS there were REAL memory leaks. When you forgot to deallocate memory, it would NEVER become accessible again until the system was reset.

在例如 AmigaOS 的美好时光中,存在真正的内存泄漏。当您忘记释放内存时,在系统重置之前它将永远无法再次访问。

I don't know of any self-respecting desktop operating system these days that would allow memory leaks to creep out of an application's virtual address space. Your mileage may vary on embedded devices when there is no extensive memory bookkeeping.

我不知道现在有任何自尊的桌面操作系统会允许内存泄漏从应用程序的虚拟地址空间中蔓延出来。当没有大量内存簿记时,您的里程可能会因嵌入式设备而异。

回答by Dan Lin

In languages like C++ that don't have garbage collection it is best practice to clean up before termination. You can do this with a destructor friend class.

在像 C++ 这样没有垃圾收集的语言中,最好的做法是在终止前进行清理。您可以使用析构函数朋友类来做到这一点。

class Singleton{
...
   friend class Singleton_Cleanup;
};
class Singleton_Cleanup{
public:
    ~Singleton_Cleanup(){
         delete Singleton::ptr;
     }
};

Create the clean up class upon starting the program and then upon exiting the destructor will be called cleaning up the singleton. This may be more verbose than letting it go to the operating system but it follows RAII principles and depending on the resources allocated in your singleton object it might be necessary.

在启动程序时创建清理类,然后在退出析构函数时将调用清理单例。这可能比让它进入操作系统更冗长,但它遵循 RAII 原则,并且可能需要根据在单例对象中分配的资源。

回答by Hans Passant

Depends on your definition of a leak. Unbound memory increase is a leak in my book, a singleton isn't unbound. If you don't provide reference counting, you intentionally keep the instance alive. Not an accident, not a leak.

取决于您对泄漏的定义。未绑定的内存增加是我书中的一个漏洞,单例不是未绑定的。如果您不提供引用计数,则您会故意使实例保持活动状态。不是事故,不是泄漏。

Your singleton wrapper's destructor should delete the instance, it is not automatic. If it just allocates memory and no OS resources, there's no point.

您的单例包装器的析构函数应该删除该实例,它不是自动的。如果它只是分配内存而没有操作系统资源,那就没有意义了。

回答by Aaron Fischer

A singletonwould be one instance of your object. This is why it does not require a counter. If it's going to exist for the length of your application then the default destructor will be fine. The memory will be, in any case, reclaimed by the operating system when the process ends.

一个单身将是你的对象的一个实例。这就是它不需要计数器的原因。如果它将在您的应用程序的长度内存在,那么默认的析构函数就可以了。在任何情况下,当进程结束时,操作系统都会回收内存。