学习笔记——C++设计模式之单例模式

C++设计模式之单例模式

_学习B站李建忠老师课程记录

动机:

1.在软件系统中,经常有这样一些特殊的类。必须保证他们在系统中只存在一个实例,才能确保他们的正确逻辑正确性、以及良好的效率。
2.如何绕过常规的的构造器,提供一种机制来保证一个类只有一个实例?
3.这应该是类设计者的责任,而不是使用者的责任。

模式定义:

保证一个类仅有一个实例,并且提供一个该实例的全局访问点。-GoF

代码:

1. 线程非安全的版本以及安全版本

class Singleton{
private://不设置则默认公有 自动设置
    Singleton(){};//拷贝构造函数
    Singleton(const Singleton& other){};//拷贝构造函数
public:
    static Singleton* getInstance();
    static Singleton* m_instance;
};

Singleton* Singleton::m_instance=nullptr;

//线程非安全版本
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

//线程安全版本,但锁的代价过高 读也在等待
Singleton* Singleton::getInstance() {
    Lock lock;
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

2. 双检查锁版本

但由于内存读写reorder不安全

#include<bits/stdc++.h>
#include<algorithm>
#include<mutex>
using namespace std;
class Singleton {
private:
    Singleton() {}
    Singleton(Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static  Singleton *m_instance;
    static std::mutex m_mutex;
public:
    static Singleton* getInstance() {
        if (m_instance == nullptr) {
            unique_lock<std::mutex> lock(m_mutex);
            if (m_instance == nullptr) {
                m_instance = new Singleton();
                static Singleton_release cl;
            }
        }
        return m_instance;
    };
    class Singleton_release
    {
    public:
        ~Singleton_release() //内嵌的清理类
            //定义了释放singleton类的静态对象 当程序结束时候会触发Singleton_release的析构 完成释放
            //Singleton的实例不能在自己本身的析构函数里写释放,因为实例是new出来的堆内存,必须手动delete
            //1、单例中的 new 的对象需要delete释放。
            //2、delete释放对象的时候才会调用对象的析构函数。
            //3、如果在析构函数里调用delete,那么程序结束时,根本进不去析构函数,怎么会delete?
            //4、如果程序结束能自动析构,那么就会造成一个析构的循坏,delete单例对象 调用析构函数 析构函数里面有delete对象 继续调用析构函数
            if (Singleton::m_instance != nullptr) {
                delete Singleton::m_instance;
                Singleton::m_instance = nullptr;
            }
        }
    };
    void func() {
        std::cout << "测试func" << endl;
    };
};
//静态变量初始化
Singleton* Singleton::m_instance = nullptr;
std::mutex Singleton::m_mutex;
int main() {
    Singleton* p_test = Singleton::getInstance();
    p_test->func();
    system("pause");
    return 0;
}

3. C++ 11版本之后的跨平台实现

#include<bits/stdc++.h>
#include<algorithm>
#include<mutex>
#include<atomic>
using namespace std;
class Singleton
{
private:
    // static volatile Singleton * volatile local_instance;
    static atomic<Singleton*> m_instance;
    static std::mutex m_mutex;
    Singleton() {
        cout << "构造" << endl;
    };
    ~Singleton() {
        cout << "析构" << endl;
    }
    class rememberFree {
    public:
        rememberFree() {
            cout << "成员构造" << endl;
        }
        ~rememberFree() {
            Singleton* local_instance = m_instance.load(std::memory_order_relaxed);
            if (local_instance != nullptr) {
                delete local_instance;
            }
        }
    };
    static rememberFree remember;//定义一个静态成员,在程序结束时系统会调用它的析构函数
public:
    static Singleton* getInstance()
    {
        Singleton* tmp = m_instance.load(std::memory_order_relaxed);//定义了m_instace原子对象 屏蔽编译器的reorder
        std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence 获取内存栅栏
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> lock(m_mutex);
            tmp = m_instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Singleton;
                std::atomic_thread_fence(std::memory_order_release);//释放内存fence
                m_instance.store(tmp, std::memory_order_relaxed);
            }
        }
        return tmp;
    }
};

atomic<Singleton*> Singleton::m_instance;
Singleton::rememberFree Singleton::remember;
std::mutex Singleton::m_mutex;
int main()
{
    cout << "Singleton第一次开始前" << endl;
    Singleton* s = Singleton::getInstance();
    cout << "Singleton访问第一次后" << endl;
    cout << "Singleton访问第二次前" << endl;
    Singleton* s2 = Singleton::getInstance();
    cout << "Singleton访问第二次后" << endl;
    return 0;
}

4. 推荐写法 Magic Static

#include <iostream>
using namespace std;
class Singleton
{
private:
    Singleton() { std::cout << "Constructor!" << std::endl; };
    Singleton(Singleton&) = delete;//禁用拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;//禁用重载赋值运算符
public:
    ~Singleton() { std::cout << "Destructor!" << std::endl; };
    static Singleton& GetInstance()//返回引用
    {
        static Singleton ins;//静态局部变量,内存中只有一个,且只会被初始化一次
        return ins;
    }
    void DoSomething();
};


void Singleton::DoSomething()
{
    std::cout << "Singleton do something\n";
}
int main()
{
    cout << "单例模式访问第一次前" << endl;
    Singleton::GetInstance().DoSomething();
    cout << "单例模式访问第一次后" << endl;
    cout << "单例模式访问第二次前" << endl;
    Singleton::GetInstance().DoSomething();
    cout << "单例模式访问第二次后" << endl;
    return 0;
}

要点总结

1.Singleton模式的实例构造器可以设置为protected以允许子类派生。
2.Singleton模式一般不要支持拷贝构造函数和Clone接口,因为这样可能导致多个对象实例,与模式的初衷相违背。
3.如何实现多线程环境下安全的singleton?注意双检查锁的正确实现或者使用magic static写法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值