快速入门设计模式Ⅲ—单例模式

单例模式

  • 定义
    保证⼀个类仅有⼀个实例,并提供⼀个该实例的全局访问点。

下面从几个单例模式的实现版本和大家探讨单例模式的实现

版本一
// 内存栈区
// 内存堆区
// 常数区
// 静态区 系统释放
// ⼆进制代码区
class Singleton {
public:
	 static Singleton * GetInstance() {
	 	if (_instance == nullptr) {
	 		_instance = new Singleton();
	 	}
	 	return _instance;
	 }
private:
	 Singleton(){}//构造
	 Singleton(const Singleton &clone){} //拷⻉构造
	 Singleton& operator=(const Singleton&) {}
	 static Singleton * _instance; 
 }
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化

存在的问题:

  • 这种写法在单线程模式下就会有内存泄漏。因为_instance在静态区,静态区由内存释放
    而new的对象在堆区
  • 线程安全问题:当多线程获取单例时有可能引发竞争。因为New的时候没加锁
版本二
class Singleton2
 {
 private:
    static Singleton2 * GetInstance() {
        if (_instance == nullptr) {
            _instance = new Singleton2();
            atexit(Destructor);
        }
        return _instance;
    }

 public:
    static void Destructor() {
        if (nullptr != _instance) {
            delete _instance;
            _instance = nullptr;
        }
    }
    Singleton2(/* args */);
    Singleton2(const Singleton2 &cpy);
    static Singleton2 * _instance;
 };

存在的问题:
这样实现可以避免单线程模式下的内存泄漏。但如果是多线程下,此时还有会线程安全的问题。

版本三、四的都是懒汉式,即需要的时候才实例化单例。

版本三——加锁
#include <mutex>

class Singleton { // 懒汉模式 lazy load
public:
    static Singleton * GetInstance() {
        // std::lock_guard<std:mutex> lock(_mutex) 在这加锁性能不好,锁的粒度大,加锁的话会涉及线程切换
        if (_instance ==nullptr) {
            std::lock_guard<std::mutex> lock(_mutex); //自旋锁 不用Unlock
            _instance = new Singleton2();           // new 个步骤:1分配内存,2调用构造函数,3赋值操作
            atexit(Destructor);
        }
        return _instance;
    }
private:
    static void Destructor() {
        if (nullptr != _instance) {
            delete _instance;
            _instance = nullptr;
        }
    }
    Singleton(){}
    Singleton(const Singleton &clone){} //拷贝构造
    Singleton& operator=(const Singleton&){} 
    static Singleton * _instance;
    static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr; //静态成员初始化

存在的问题:

  • 这个版本其实是存在问题的,在New的时候:1分配内存,2调用构造函数,3赋值操作,多线程环境下cpu进行重排 1 3 2 这时候会有线程问题
版本四——使用cpp11的原子类
#include <mutex>
#include <atomic>
class Singleton {
public:
    static Singleton * GetInstance() {
        Singleton *tmp = _instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire); // 获取内存屏障,防止reorder
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> lock(_mutex);
            if (tmp == nullptr) {
                tmp = new Singleton;
                std::atomic_thread_fence(std::memory_order_release); // 释放内存屏障
                _instance.store(tmp, std::memory_order_release);
                atexit(Destructor);
            }
        }
        return tmp;
    }
private:
    static void Destructor() {
        Singleton *tmp = _instance.load(std::memory_order_relaxed);
        if (tmp != nullptr) {
            delete tmp;
        }
    }
    Singleton(){}
    Singleton(const Singleton &clone){} //拷贝构造
    Singleton& operator=(const Singleton&){} 
    static std::atomic<Singleton *> _instance;
    static std::mutex _mutex;
}
 //静态成员初始化
std::atomic<Singleton *> Singleton::_instance;
std::mutex Singleton::_mutex;

需要注意的点就是在编译的时候加上c++11即可:g++ Singleton.cpp -o singleton -std=c++11

版本五——静态局部变量(c++11)

在c++11的static自带线程安全,如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。如果不是c++11的话还是会有线程安全问题哦~
代码如下:

template<typename T>
class Singleton {
public:
    static T& GetInstance(){
        static Singleton instance;
        return instance;
    }

    virtual ~Singleton(){}

protected:
    Singleton(){}
    Singleton(const Singleton&) {}
    Singleton& operator = (const Singleton&) {}
}

class DesignPattern:public Singleton<DesignPattern> {
    friend class Singleto<DesignPattern>; // friend可以让Singleton<DesignPattern>可以访问到自己私有的成员
private:
    DesignPattern(){}
    DesignPattern(const DesignPattern &clone){} //拷贝构造
    DesignPattern& operator=(const DesignPattern&){}     
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值