静态字段的析构函数。单例实现

Posted

技术标签:

【中文标题】静态字段的析构函数。单例实现【英文标题】:Destructor for static fields. Singleton realization 【发布时间】:2012-04-15 15:00:34 【问题描述】:

所以,经典的简单单例实现如下:

class Singleton

private:
    static Singleton* singleton;
    Singleton() 
public:
    static Singleton* getInstance();        
;

cpp 文件

Singleton* Singleton::singleton = 0;

Singleton* Singleton::getInstance()

    if (!singleton)
    
        singleton = new Singleton;
    

    return singleton;

我在这里看到内存泄漏 - '因为 new 没有 delete。但是在 C++ 中没有静态析构函数,所以我们不关心这个内存泄漏?

【问题讨论】:

预计单例对程序的整个生命周期都有用。 见C++ Singleton design pattern(或者只在SO上搜索singleton,你会发现很多信息) 这看起来是用 C# 重写的 Singleton 实现:msdn.microsoft.com/en-us/library/ff650316.aspx 但在 C# 中,“new”不需要“delete”。 @SChepurin 这个例子来自 GoF 书 @Hate.Nothing 是完美的。关于 Singleton 的删除有很多要讨论的“Singleton 是如何被删除的?”:sourcemaking.com/design_patterns/to_kill_a_singleton 【参考方案1】:

内存泄漏不仅仅是没有匹配空闲的分配。当您拥有可以回收的内存时,因为该对象不再使用,但实际上并没有被释放。事实上,许多内存泄漏是程序中有代码释放内存的情况,但无论出于何种原因,它都没有被调用(例如,引用循环)。有很多关于如何检测这些泄漏的研究。 this paper 是此类工具的一个很好的例子。

在单例的情况下,我们没有泄漏,因为该单例存在于整个程序中。它的生命周期永远不会结束,因此没有被回收的内存不是问题。

也就是说,您上面的代码并不是大多数人会实现单例的方式。规范的 C++ 实现是这样的:

class Singleton

private:
    /* No instantiation. */
    Singleton() 

    /* Explicitly disallow copying. */ 
    Singleton(const Singleton&) = delete;
    Singleton& operator= (const Singleton&) = delete;

    /* In C++03, the above would be written as
     *
     *    Singleton(const Singleton&);
     *    Singleton& operator= (const Singleton&);
     * 
     * and you'd just leave the methods unimplemented.
     */
public:
    static Singleton& getInstance();        
;

.cpp 文件:

Singleton& Singleton::getInstance() 
    /* Have a static local variable representing the unique instance.  Since
     * it's static, there is only one instance of this variable.  It's also only
     * initialized when getInstance is called.
     */
    static Singleton theInstance;
    return theInstance;

现在根本没有动态分配 - 内存由编译器分配,并且可能驻留在代码或数据段中,而不是在堆中。另请注意,您必须明确禁止复制,否则您可能会得到许多单例的克隆。

这样做的另一个优点是 C++ 保证在程序退出时(假设程序正常终止),theInstance 的析构函数确实会在程序结束时触发。因此,您可以使用所需的所有清理代码定义析构函数。

希望这会有所帮助!

【讨论】:

什么是=删除;你不是每次调用 getInstance() 时都声明新的静态 theInstance 吗? @Hate-= delete; 是一种新的 C++11 语法,用于显式标记函数不存在且无法调用。在 C++03 中,您只需要声明这些函数而不是实现它们。此外,您每次都没有获得实例的新副本的原因是局部变量标记为static,这意味着该变量仅存在一个副本,并且它在函数调用中持续存在。这是否回答你的问题?或者还有什么我可以澄清的吗? 我看到那个静态单例实例;创建静态变量,我知道静态变量在程序运行时一直退出。但是当你第二次调用这个函数时,它不会愿意创建新的静态变量 theInstance 并且不会出现重定义错误吗? @Hate- 不。静态局部变量与静态全局变量类似,实际上只存在一个副本。如果多次调用该函数,它总是引用同一个静态局部变量。有关详细信息,请参阅***.com/questions/246564/…。 当我写在主类Test ;静态测试测试;静态测试测试;它让我测试重新定义错误..【参考方案2】:

deletenew 没有匹配时,为什么要避免使用此类代码

虽然没有实际的内存泄漏(在大多数现代操作系统中),但更糟糕的是你的Singleton 析构函数没有被调用。如果你获得一些资源,它们可能泄漏。

这里可以做什么

使用智能指针存储实例,考虑std::unique_ptr(使用C++11)或boost::auto_ptr

【讨论】:

@Lol4t0- 我代码中的版本也是延迟初始化的; static 局部变量仅在首次调用包含它们的函数时才被初始化。 @templatetypedef,你在函数里面定义了,那好吧。 @Lol4t0:你和 templatetypedef 给出了两个相互矛盾的陈述:你说单例析构函数没有被调用,而他们说析构函数在程序完成时被触发。既然这正是我来这里发现的,你介意确认还是否认你的说法? @Larry:这取决于单例实现。问题正文中的实现不会触发析构函数,但 templatetypedef 的实现会触发。 @Lol4t0 谢谢!所以看起来动态内存没有被破坏,只是被释放了。【参考方案3】:

当函数的局部变量被声明为“静态”时,这意味着它没有在堆栈上分配 - 并且它的值从一次调用到下一次调用都保持不变。

【讨论】:

以上是关于静态字段的析构函数。单例实现的主要内容,如果未能解决你的问题,请参考以下文章

c++单例模式为啥不在析构函数中释放静态的单例对象,而要加一个内嵌类

静态对象成员会在所属类的析构函数被调用时自动析构吗?

重读STL源码剖析:析构

基类的析构函数写成virtual虚析构函数

C# 使用 AWS lambda 时,我可以确定函数中的析构函数会被执行吗?

为什么基类的析构函数要写成虚函数?