C++设计模式 | Singleton单例模式

单例模式概念

 

单例模式:保证一个类仅有一个实例化对象,并且提供一个访问它的全局访问点。

就是说无论怎么去构造对象,该类只能实例化一个对象,并且是一个全局访问的。因此可以根据它的概念去进行实现

实现逻辑:

1.构造函数私有,这样用户就不能构造出对象

2.定义唯一一个静态对象

3.实现一个公共接口访问静态对象

实现方式:饿汉与懒汉

饿汉单例模式:程序启动时就实例化唯一一个对象,在类定义时就创建一个唯一对象。

懒汉单例模式:在第一次调用使用对象时在进行创建唯一的实例化对象,实现延迟加载效果

饿汉单例模式

代码

class Singleton
{
public:
    static Singleton *getInstance()
    {
        return &instance;
    }
private:
    Singleton() { cout << "Singleton()" << endl; }
    Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
    static Singleton instance;
};
Singleton Singleton::instance;
int main()
{
    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();
    Singleton *p3 = Singleton::getInstance();

    cout << p1 << " " << p2 << " " << p3 << endl;
    return 0;
}

从上面结果可以看到申请的三个对象都是同一个对象


 

懒汉单例模式

普通的懒汉单例模式

class Singleton
{
public:
    static Singleton *getInstance()
    {
        if (instance == nullptr)
        {
            instance = new Singleton();
        }
        return instance;
    }

private:
    static Singleton *instance;
    Singleton() { cout << "Singleton()" << endl; }
    Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
    ~Singleton() { cout << "~Singleton()" << endl; }

    class Release
    {
    public:
        ~Release()
        {
            if (instance != nullptr)
            {
                delete instance;
                instance = nullptr;
            }
        }
    };
    static Release del;
};
Singleton *Singleton::instance = nullptr;
Singleton::Release Singleton::del;

int main()
{
    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();
    cout << p1 << " " << p2 << endl;
    return 0;
}

可以看到也只能申请到一个对象

由于在getInstance()是一个不可重入函数     if (instance == nullptr) {  instance = new Singleton(); }   是一个非原子操作,因此在多线程环境下是一个非线程安全的单例模式

线程安全的懒汉单例模式

使用互斥锁来保证线程安全

class Singleton
{
public:
    static Singleton *getInstance()
    {
        pthread_mutex_lock(&_mutex);
        if (instance == nullptr)
        {
            instance = new Singleton();
        }
        pthread_mutex_unlock(&_mutex);
        return instance;
    }

private:
    Singleton()
    {
        cout << "Singleton()" << endl;
    }
    Singleton(const Singleton &instance)
    {
        cout << "Singleton(const Singleton&)" << endl;
    }
    ~Singleton()
    {
        pthread_mutex_destroy(&_mutex);
        cout << "~Singleton()" << endl;
    }

    class Release
    {
    public:
        ~Release()
        {
            if (instance != nullptr)
            {
                delete instance;
                instance = nullptr;
            }
        }
    };
    static Release rel;

    static Singleton *instance;
    static pthread_mutex_t _mutex;
};
Singleton *Singleton::instance = nullptr;
pthread_mutex_t Singleton::_mutex = PTHREAD_MUTEX_INITIALIZER;

Singleton::Release Singleton::rel;

int main()
{
    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();
    cout << p1 << " " << p2 << endl;
    return 0;
}

优化:双重判断+锁

上述代码由于使用锁进行了频繁的加锁解锁操作,导致效率不高,我们只需要在第一次调用时进行加锁解锁操作就可以了,因此需要进行优化。

针对getInstace()进行修改

    static Singleton *getInstance()
    {
        if (instance == nullptr)
        {
            pthread_mutex_lock(&_mutex);
            /*
            此时需要在进行一次判空
            原因:有多个线程在第一次判断空时进入了第一个if语句
            此时如果不在进行判空 在解锁后会有线程再次去进行new对象
            */
            if (instance == nullptr)
            {
                instance = new Singleton();
            }
            pthread_mutex_unlock(&_mutex);
        }

        return instance;
    }

问题:在外面已经进行一次instance判空 为什么加锁后还要进行判空?

基于OOP思想的懒汉单例模式

我们将锁进行封装,这样就会对代码进行解耦,使得耦合性降低

class Cmutex
{
public:
    Cmutex() { pthread_mutex_init(&_mutex, nullptr); }
    ~Cmutex() { pthread_mutex_destroy(&_mutex); }
    void lock() { pthread_mutex_lock(&_mutex); }
    void unlock() { pthread_mutex_unlock(&_mutex); }

private:
    pthread_mutex_t _mutex;
};
class Singleton
{
public:
    static Singleton *getInstance()
    {
        if (instance == nullptr)
        {
            _mutex.lock();
            if (instance == nullptr)
            {
                instance = new Singleton();
            }
            _mutex.unlock();
        }

        return instance;
    }

private:
    Singleton() { cout << "Singleton()" << endl; }
    Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
    ~Singleton() { cout << "~Singleton()" << endl; }

    //自动析构instance类
    class Release
    {
    public:
        ~Release()
        {
            if (instance != nullptr)
            {
                delete instance;
                instance = nullptr;
            }
        }
    };
    static Release rel;

    static Singleton *instance;
    static Cmutex _mutex;
};
Singleton *Singleton::instance = nullptr;
Singleton::Release Singleton::rel;
Cmutex Singleton::_mutex;
int main()
{
    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();
    cout << p1 << " " << p2 << endl;
    return 0;
}

使用内部静态变量保证线程安全

class Singleton
{
public:
    static Singleton* getInstance()
    {
        static Singleton single;
        return &single;
    }
private:
    static Singleton *single;
     Singleton() { cout << "Singleton()" << endl; }
     Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
     ~Singleton() { cout << "~Singleton()" << endl; }
};
int main()
{
    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();
    cout << p1 << " " << p2 << endl;
    return 0;
}

使用GDB调试查看汇编代码可以看到对静态成员进行初始化时,编译器进行了加锁解锁的保护,因此他是一个线程安全的单例模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值