饿汉模式的对象在类产生时候就创建了,一直到程序结束才会去释放。即作为一个单例类实例,它的生存周期和我们的程序一样长。因此该实例对象需要存储在全局数据区,所以肯定需要使用static来修饰,因为类内部的static成员是不属于每个对象的,而是属于整个类的。

如果有多个动态链接库,链接库中都引用了单例类,每个单例类在动态链库中都是唯一的,这个就和单例的期望不一样(单例不唯一了),如果要唯一的话,加上 -rdynamic 编译参数

手动释放

#ifndef SINGLETON_H
#define SINGLETON_H

/* 饿汉式
 * 线程安全,稍浪费内存,空间换时间。我推荐用这种
 * 懒汉式不推荐
 * 在懒汉模式中,如果用到双检查锁,请注意双检查锁中内存reorder引发的问题
 *
 * */

#include <iostream>

class Singleton
{
    private:
        Singleton(){std::cout << "construct it" << std::endl;}
        Singleton(const Singleton&) = delete;
        Singleton(Singleton&&) noexcept = delete;
        Singleton& operator=(const Singleton&) = delete;
        Singleton& operator=(Singleton&&) noexcept = delete;
          
        //析构函数我们也需要声明成private的
        //因为我们想要这个实例在程序运行的整个过程中都存在
        //所以我们不允许实例自己主动调用析构函数释放对象
        ~Singleton() { std::cout << "in deconstructor" << std::endl;};

    private:
        static Singleton* m_pSingleton;
    public:
        static Singleton* getInstance();
        static void freeInstance();
};
#endif // SINGLETON_H

#include "singleton.h"

// 静态变量是一个指针
// 程序运行完成后,不会自动删除指向的堆内存(这好像是常识),哈哈
// 所以在最后必须调用 delete
Singleton* Singleton::m_pSingleton = new Singleton();


Singleton* Singleton::getInstance()
{
    
    return m_pSingleton;

}

void Singleton::freeInstance()
{
    std::cout << "free it" << std::endl;
    if (NULL != m_pSingleton)
    {
        delete m_pSingleton;  // 在静态函数中delete一个对象,当然也会调用这个对象的析构函数
        m_pSingleton = NULL;
    }

}

#include <iostream>
#include "singleton.h"


int main(int argc, char** argv)
{
    
    Singleton* p1 = Singleton::getInstance();
    Singleton* p2 = Singleton::getInstance();

    if (p1 == p2)
    {
        std::cout << "success" << std::endl;
    }else
    {
        std::cout << "failed" << std::endl;
    }

    Singleton::freeInstance();

    return 0;
}
/*
construct it
success
free it
in deconstructor
*/

智能指针

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

 
class Singleton{
private:
    Singleton(){
        cout << "创建了一个单例对象" << endl;
    }
    
 Singleton(const Singleton&) = delete;
 Singleton(Singleton&&) noexcept = delete;
 Singleton& operator=(const Singleton&) = delete;
 Singleton& operator=(Singleton&&) noexcept = delete;
    
    ~Singleton(){
        // 析构函数我们也需要声明成private的
        // 不允许实例自己主动调用析构函数释放对象,比如 delete p_obj
        cout << "销毁了一个单例对象" << endl;
    }
    
private:

    static std::shared_ptr<Singleton> instance; //这是我们的单例对象,它是一个类对象的指针
    
public:
    static std::shared_ptr<Singleton> getInstance();
};
    
 
// 下面这个静态成员变量在类加载的时候就已经初始化好了
std::shared_ptr<Singleton> Singleton::instance(new Singleton(), [](Singleton* t){delete t;}); // lambda表达式就可以
// std::shared_ptr<Singleton> Singleton::instance(new Singleton()) // 因为默认访问不了private 析构函数
 
std::shared_ptr<Singleton> Singleton::getInstance(){
    return instance;    
}


int main()
{
    cout << "Now we get the instance" << endl;
    std::shared_ptr<Singleton> instance1 = Singleton::getInstance();
    std::shared_ptr<Singleton> instance2 = Singleton::getInstance();
    std::shared_ptr<Singleton> instance3 = Singleton::getInstance();
    cout << "Now we destroy the instance" << endl;
    return 0;
}

/*
创建了一个单例对象
Now we get the instance
Now we destroy the instance
销毁了一个单例对象
*/


/*
https://stackoverflow.com/questions/14801591/calling-private-destructor-as-deleter-for-stdshared-ptr-using-lambda


The standard says in 5.1.2/3

    The type of the lambda-expression [...] is a unique [...] class type — called the closure type [...] The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.

This means that a lambda that occurs inside a (member) function is treated like a local class, declared at block scope in the surrounding function. About local classes, the standard says in 9.8/1:

    [...] The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function.[...]

Thus the lambda has the same access as the containing member function, which means that it can access private members of the class.

If a lambda occurs directly in a class scope, it would be treated as a nested class, for which a similar rule applies: 11.7/1 says:

    A nested class is a member and as such has the same access rights as any other member.

Either way, a lambda that occurs within in the scope of a class has access to private class members. So your example is fine.

(The post you referred to, ultimately was about a problem accessing protected members of base classes named by a qualified-id.)

*/

C++11 懒汉模式

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are inaccessible(especially from outside), otherwise, you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐