zoukankan      html  css  js  c++  java
  • 单例模式

    单例模式产生的原因:

    每个类都能产生很多对象,而在某些场景下,我们仅需要一个实例,所有的操作都针对这个单例来进行。

    实际构建中,我们需要保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    常用两种:

    懒汉式:

    class single
    {
    public:
        ~single()
        {
            if(mInstance)
            {
                delete mInstance;
            }
        };
        static single* getInstance()
        {
            if(mInstance == NULL)
            {
                mInstance = new single();
            }
            return mInstance;
        }
        void printArr()
        {
            printf("%p  ",this);
        }
    private:
        single(){};
        single(const single&);
        single& operator = (const single&);
        static single* mInstance;
    };
    single* single::mInstance = NULL;
    void main()
    {
        single::getInstance()->printArr();
        single::getInstance()->printArr();
        cin.get();
    }

    得到结果: 00785D70  00785D70

    我们的到这个实例后任何的操作都是基于这单个的对象进行。

    先讲一下为什么是static的数据成员mInstance和static的函数成员getInstance

    是为了把这个对象存放在全局的存储区内,供所有需要调用的地方调用。

    另外补充一下,静态数据成员(非静态常量成员)不能在类中初始化,因为静态数据成员为所有的该类对象所共有,没有必要在每个类构建对象时去重新赋值,这与所有该类维护同一份static变量本身也是违背的。

    另外为了保证单例的唯一性,我把它的构造函数定义在了private部分,另外将它的拷贝构造函数和赋值函数也声明为私有,这样避免了除了getIntance()得到类的对象其他所有通过隐式构造产生对象的可能。

    这种懒汉式的单例模式在单线程中是毫无问题的,但在多线程中仍存在问题,那就是两个线程可能同时判断为NULL,这样就new了两个对象。

    饿汉式:

    class single
    {
    public:
        ~single()
        {
            if(mInstance)
            {
                delete mInstance;
            }
        };
        static single* getInstance()
        {
            return mInstance;
        }
        void printArr()
        {
            printf("%p  ",this);
        }
    private:
        single(){};
        single(const single&);
        single& operator = (const single&);
        static single* mInstance;
    };
    single* single::mInstance = new single();

    在程序启动时即去new这个实例的指针,后续调用时也就不存在同时为NULL的这种情景。懒汉式虽然存在线程安全的问题,确可以节省空间。工作中为了避免线程安全问题和懒得加锁,一般还是饿汉式的省心一点。不过在linux平台上,跑的时候偶尔发现main函数调用到

    getInstance()了,全局外部的new还没有new出来导致使用了空指针,至今还没搞明白,可能cpp文件比较多,初始化跑的顺序比单个文件复杂一些。

    因此在此基础上还有一种加锁的懒汉式单例写法:

    方式一:两次判空,getInstance()中,第一次判空的时候加锁,第二次判空new 对象,new 完之后解锁

    方式二:利用静态变量只初始化一次的特性构建单例,懒得写了,都差不多,copy一份

    这里仍然要注意的是局部变量初始化的线程安全性问题,在C++0X以后,要求编译器保证静态变量初始化的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。

    class Singleton
    {
    public:
        static Singleton* GetInstance()
        {
            Lock(); // not needed after C++0x 
            static Singleton instance;  
            UnLock(); // not needed after C++0x 
    
            return &instance;
        }
    
    private:
        Singleton() {};
        Singleton(const Singleton &);
        Singleton & operator = (const Singleton &);
    };
    其中lock()为线程锁锁,UnLock为线程锁解锁,两个线程同时在跑时,当一边lock住时,另一线程将处于等待状态,unlock时,结束等待,这样就避免了同时new这种情况的产生

    单例是各种设计模式中经常用到的且非常好用的类对象的调用模式。


  • 相关阅读:
    问题大全
    redis学习笔记-03:redis安装
    redis学习笔记-02:为什么使用NoSQL数据库
    redis学习笔记-01:redis简介
    docker学习笔记-05:Docker安装mysql和redis
    docker学习笔记-06:自定义DockerFile生成镜像
    docker学习笔记-05:DockerFile解析
    全栈之路-杂篇-JPA多对多配置分析
    全栈之路-小程序API-JWT令牌详细剖析与使用
    全栈之路-小程序API-Json数据类型的序列化与反序列化
  • 原文地址:https://www.cnblogs.com/doulcl/p/10046090.html
Copyright © 2011-2022 走看看