单例:是否存在内存泄漏

Singleton: is there a memory leak?

本文关键字:内存 泄漏 存在 是否 单例      更新时间:2023-10-16

这是一个简单的单例:

class Singleton
{
    Singleton();
    virtual ~Singleton();
    Singleton * Singleton::getInstance() 
    {
        static Singleton * instance;
        if (!instance) {
            instance = new Singleton();
        };
        return instance;
    };
}

当主代码第一次调用Singleton::getInstance()->someMethod()时,类不是实例化两次吗?会有内存泄漏吗?

我问,因为视觉泄漏检测器检测内存泄漏与new Singleton()

当主代码第一次调用Singleton::getInstance()->someMethod(),是不是类实例化两次?

。调用Singleton的静态成员不会实例化Singleton,因此这里存在的Singleton的唯一实例是您用new创建的实例。而且你只创建一次,因为一旦instance指向它,你就再也不会调用new了。

这里有一个问题,但是,您没有使getInstance成为静态成员函数。我认为这是一个打字错误/疏忽,因为,否则,你的程序甚至不会编译。事实上,即使作为非静态成员,声明也是错误的。此外,构造函数应该是private,以强制只有getInstance可以实例化类型。

会有内存泄漏吗?

是的,这就是泄漏检测器报告的内容。然而,它是最小的:问题是在程序关闭之前没有任何东西可以delete这个单例实例。

坦率地说,我不会担心的。这个可能是可以接受泄漏的极少数情况之一,主要是因为它不仅仅是一个"泄漏",它只是在进程关闭时取消分配的一次性失败。

然而,如果你完全摆脱指针,那么你可以同时避免这两个问题,因为你的程序做的最后一件事将是销毁静态存储时间的对象:

#include <iostream>
class Singleton
{
public:
    ~Singleton()  { std::cout << "destruction!n"; }
    static Singleton& getInstance() 
    {
        static Singleton instance;
        return instance;
    }
    void foo() { std::cout << "foo!n"; }
private:
    Singleton() { std::cout << "construction!n"; }
};
int main()
{
    Singleton::getInstance().foo();
}
// Output:
//   construction!
//   foo!
//   destruction!

(现场演示)

甚至不需要指针!

这有一个额外的好处,即整个函数本质上是线程安全的,至少在c++ 11中是这样。

@LightnessRacesInOrbit回答正确,切中要害。既然你的问题在某种程度上是关于实现单例时的最佳实践的(顺便说一句,你应该尽量避免),我将给出我的看法。你可以使用一种叫做好奇循环模板模式的东西来创建一个Singleton基类,它将你想要创建Singleton的类作为模板参数。一旦您正确地完成了这些,创建singleton就像在公园里散步一样容易。只要从Singleton<Foo>派生Foo,并将Foo::Foo()Foo::~Foo()设为私有。下面是我使用的代码,带有注释,live on Coliru:

// Singleton pattern via CRTP (curiously recurring template pattern)
// thread safe in C++11 and later
#include <iostream>
#include <type_traits>
// generic Singleton via CRTP
template <typename T>
class Singleton
{
protected:
    Singleton(const Singleton&) = delete; // to prevent CASE 3
    Singleton& operator=(const Singleton&) = delete; // to prevent CASE 4
    Singleton() noexcept = default; // to allow creation of Singleton<Foo>
    // by the derived class Foo, since otherwise the (deleted)
    // copy constructor prevents the compiler from generating
    // a default constructor;
    // declared protected to prevent CASE 5
public:
    static T& get_instance()
    noexcept(std::is_nothrow_constructible<T>::value)
    {
        static T instance;
        return instance;
    }
    // thread local instance
    static thread_local T& get_thread_local_instance()
    noexcept(std::is_nothrow_constructible<T>::value)
    {
        static T instance;
        return instance;
    }
};
// specific Singleton instance
// use const if you want a const instance returned
// make the constructor and destructor private
class Foo: public Singleton</*const*/ Foo>
{
    // so Singleton<Foo> can access the constructor and destructor of Foo
    friend class Singleton<Foo>;
    Foo() // to prevent CASE 1
    {
        std::cout << "Foo::Foo() private constructor" << std::endl;
    }
    // OK to be private, since Singleton<Foo> is a friend and can invoke it
    ~Foo() // to prevent CASE 2
    {
        std::cout << "Foo::~Foo() private destructor" << std::endl;
    }
public:
    void say_hello()
    {
        std::cout << "t Hello from Singleton" << std::endl;
    }
};
int main()
{
    Foo& sFoo = Foo::get_instance();
    sFoo.say_hello();
    Foo& sAnotherFoo = Foo::get_instance(); // OK, get the same instance
    sAnotherFoo.say_hello();
    Foo& sYetAnotherFoo = sFoo; // still OK, get the same instance
    sYetAnotherFoo.say_hello();
    thread_local Foo& stlFoo = Foo::get_thread_local_instance(); // thread local instance
    stlFoo.say_hello();
    // CASE 1: error: 'Foo::Foo()' is private
    // Foo foo;
    // Foo* psFoo = new Foo;
    // CASE 2: error: 'Foo::~Foo()' is private
    Foo* psFoo = &Foo::get_instance(); // can get pointer
    psFoo->say_hello();
    // delete psFoo; // cannot delete it
    // CASE 3: error: use of deleted function 'Foo::Foo(const Foo&)'
    // Foo foo(sFoo);
    // CASE 4: error: use of deleted function 'Foo& Foo::operator=(const Foo&)'
    // sFoo = sAnotherFoo;
    // CASE 5: error: 'Singleton<T>::Singleton() [with T = Foo]' is protected
    // Singleton<Foo> direct_sFoo;
}

new Singleton()没有匹配的delete,所以是的,您正在泄漏资源。

当程序关闭时,您将获得内存,但是当程序结束时,并不是所有的资源都返回。

您可以通过将实例设置为std::auto_ptrstd::unique_ptr来修复它。或者不要使用指针