c++单例模式-6种单例层层迭代优化

简介: 6种单例模式,层层迭代优化

定义
单例模式(Singleton Pattern,也称为单件模式),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
他应该满足以下几点:

1:确保一个类只有一个实例被建立     

2:构造函数应该声明为非公有,从而禁止外界创建实例。
3:拷贝操作和移动操作也应该禁止。
4:提供公有方法获取实例
5:支持多线程

版本1(懒汉)
缺点:
1:没有对资源进行释放
2:不支持多线程

    class Singleton {
   
    public:
    static Singleton * GetInstance() {
   
        if (_instance == nullptr) {
   
            _instance = new Singleton();
        }
        return _instance;
        }
        private:
        Singleton(){
   }//构造
        Singleton(const Singleton &clone){
   } //拷⻉构造
        Singleton& operator=(const Singleton&) {
   }
        stati    c Singleton * _instance;
    }
    Singleton* Singleton::_instance = nullptr;//静态成员需要初始化

版本2
缺点:
1:不支持多线程

    class Singleton {
   
    public:
    static Singleton * GetInstance() {
   
        if (_instance == nullptr) {
   
            _instance = new Singleton();
            atexit(Destructor);
        }
        return _instance;
        }
        ~Singleton() {
   }
        private:
        static void Destructor() {
   
            if (nullptr != _instance) {
   
            delete _instance;
            _instance = nullptr;
        }
        }
        Singleton();//构造
        Singleton(const Singleton &cpy); //拷⻉构造
        Singleton& operator=(const Singleton&) {
   }
        static Singleton * _instance;
    }
    Singleton* Singleton::_instance = nullptr;//静态成员需要初始化

版本3(双重校验锁)
此代码是否感觉很安全,但实际上有重大问题
首先new一个对象时,分为三个步骤,1分配内存 2构造对象 3返回指针。所以常规顺序是123,但是编译器有时候为了优化会指令重排,导致顺序变成132,这里我们考虑这么一种情况
A线程:new1分配内存并按照指令重拍之后的顺序将2地址返回_instance 指针(这时候_instance 指针是非空的,但是还没有到达构造构造,此时时间片用完,线程切换)
B线程进入,此时发现_instance 是非空的,便直接将_instance返回给调用者,而调用着使用这个没有构造的对象将会发生不可预测的后果

指令重拍原因很多,比如编译器优化或者cpu指令流水线导致,具体可以查阅相关资料

    /****************************************
    static Singleton * GetInstance();
    加锁位置:由于除了第一次是需要new,后面调用都只是获取实例,所以在3.1处加锁会极大的损耗性能,故在3.2处通过if判断后才加锁,
    两次if判空原因:主要是考虑多线程环境线程切换因素
    *****************************************/
    #include <mutex>
    class Singleton {
    // 懒汉模式 lazy load
    public:
    static Singleton * GetInstance() {
   
        //std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程
        if (_instance == nullptr) {
   
            std::lock_guard<std::mutex> lock(_mutex); // 3.2
            if (_instance == nullptr) {
   
            _instance = new Singleton();
            atexit(Destructor);
            }
        }
        return _instance;
    }
    private:
        static void Destructor() {
   
            if (nullptr != _instance) {
   
                delete _instance;
                _instance = nullptr;
            }
        }
        Singleton(){
   } //构造
        Singleton(const Singleton &cpy){
   } //拷⻉构造
        Singleton& operator=(const Singleton&) {
   }
        static Singleton * _instance;
        static std::mutex _mutex;
    }
    Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
    std::mutex Singleton::_mutex; //互斥锁初始化

版本4
解决版本三问题,但是代码显得很臃肿

    #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);//获取内存屏障
            if (tmp == nullptr) {
   
                std::lock_guard<std::mutex> lock(_mutex);
                tmp = _instance.load(std::memory_order_relaxed);
                if (tmp == nullptr) {
   
                    tmp = new Singleton;
                    std::atomic_thread_fence(std::memory_order_release);//释放内存屏障
                    _instance.store(tmp, std::memory_order_relaxed);
                    atexit(Destructor);
                }
            }
            return tmp;
        }
    private:
        static void Destructor() {
   
        Singleton* tmp = _instance.load(std::memory_order_relaxed);
        if (nullptr != tmp) {
   
            delete tmp;
        }
    }
        Singleton(){
   }
        Singleton(const Singleton&) {
   }
        Singleton& operator=(const Singleton&) {
   }
        static std::atomic<Singleton*> _instance;
        static std::mutex _mutex;
    };
    std::atomic<Singleton*> Singleton::_instance;//静态成员需要初始化
    std::mutex Singleton::_mutex; //互斥锁初始化

版本5
c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束。
缺点:无法继承

    class Singleton
    {
   
    public:
        ~Singleton(){
   }
        static Singleton& GetInstance() {
   
            static Singleton instance;
            return instance;
    }
    private:
        Singleton(){
   }
        Singleton(const Singleton&) {
   }
        Singleton& operator=(const Singleton&) {
   }
    };

版本6

    template<typename T>
    class Singleton {
   
    public:
        static T& GetInstance() {
   
            static T instance; // 这⾥要初始化DesignPattern,需要调⽤DesignPattern 构造函数,同时会调⽤⽗类的构造函数。
            return instance;
        }
    protected:
        virtual ~Singleton() {
   }
        Singleton() {
   } // protected修饰构造函数,才能让别⼈继承
        Singleton(const Singleton&) {
   }
        Singleton& operator =(const Singleton&) {
   }
    };
    class DesignPattern : public Singleton<DesignPattern> {
   
        friend class Singleton<DesignPattern>; // friend 能让 Singleton<T> 访问到 DesignPattern构造函数
    private:
        DesignPattern(){
   }
        DesignPattern(const DesignPattern&) {
   }
        DesignPattern& operator=(const DesignPattern&) {
   }
    }
目录
相关文章
|
2月前
|
安全 编译器 程序员
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
60 2
|
2月前
|
C++
C++单例模式
C++中使用模板实现单例模式的方法,并通过一个具体的类A示例展示了如何创建和使用单例。
37 2
|
2月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化2
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
78 6
|
2月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化1
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
94 7
|
7月前
|
设计模式 安全 算法
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
59 0
|
4月前
|
安全 C++
C++ QT 单例模式
C++ QT 单例模式
86 0
|
4月前
|
设计模式 安全 IDE
C++从静态类型到单例模式
C++从静态类型到单例模式
40 0
|
5月前
|
设计模式 安全 C++
C++一分钟之-C++中的设计模式:单例模式
【7月更文挑战第13天】单例模式确保类只有一个实例,提供全局访问。C++中的实现涉及线程安全和生命周期管理。基础实现使用静态成员,但在多线程环境下可能导致多个实例。为解决此问题,采用双重检查锁定和`std::mutex`保证安全。使用`std::unique_ptr`管理生命周期,防止析构异常和内存泄漏。理解和正确应用单例模式能提升软件的效率与可维护性。
70 2
|
6月前
|
编译器 C++ 开发者
C++一分钟之-返回值优化与Move Semantics
【6月更文挑战第19天】C++的RVO与移动语义提升效率,减少对象复制。RVO是编译器优化,避免临时对象的创建。移动语义通过右值引用和`std::move`转移资源所有权。注意RVO不是总有效,不应过度依赖。使用移动语义时,避免误用`std::move`导致对象无效。示例展示了RVO和移动构造函数的应用。理解并恰当使用这些机制能写出更高效代码。
76 3
|
6月前
|
设计模式 存储 缓存
C++ -- 单例模式
**摘要:** 单例模式确保一个类仅有一个实例,并提供全局访问点。为了实现单例,构造函数通常设为私有,通过静态成员函数来创建和返回实例。两种常见实现是饿汉模式(在类加载时创建实例,线程安全但可能导致不必要的内存占用)和懒汉模式(首次使用时创建,可能需线程同步)。拷贝构造函数和赋值运算符通常被禁用来防止额外实例的创建。单例模式适用于资源管理、缓存和线程池等场景。在C++中,静态成员变量和函数用于存储和访问单例实例,保证其生命周期与程序相同。