C++单例

       C++单例中也存在线程安全问题,这篇文章总结单例模式的各种创建方式,讨论利弊。单例的目的是为实现结构对象中只有一个实例,对于类的构造函数,拷贝构造函数、赋值构造函数应当做对应处理。普通构造函数和析构函数 应为private 级别,拷贝构造函数和赋值构造函数应当删除,防止构造多个对象。以下分析懒汉式、饿汉式、call_once 等构造方式及利弊分析。

1. 懒汉式

//MyClass.h

class MyClass{
    public:
    static MyClass* getInstance(){
            if (m_instance == nullptr) {
                m_instance = new MyClass();            
            }
            return m_instance;
    }
    private:
        MyClass() {} //私有构造函数
        MyClass(const MyClass& my);//禁止拷贝构造函数
        MyClass& operator=(const MyClass &my); //禁止赋值操作
        static MyClass* m_instance;
}

//MyClass.cpp
MyClass* MyClass::m_instance = nullptr;// 存放在Cpp中,若放在.h中,.h被包含多次时重复定义

上面例子存在的问题是:

  • 线程不安全
  • 不能灵活析构单例

解决方案: 在getInstance()中使用m_instance时加锁

class MyClass{
private:
     std::unique_lock  locker(m_mutex);  
} 
public:
      static MyClass* getInstance(){
        std::unique_lock locker(m_mutex);
        if (m_instance == nullptr) {
            m_instance = new MyClass();            
        }
        return m_instance;
    }
    static void destory(){
        std::unique_lock locker(m_mutex);
        if (m_instalce) {
            delete m_instance;
            m_instance = nullptr;
        }
    }

以上代码解决了线程安全和单例内存释放的问题,但是如果单例对象被多次访问需要多次加锁,效率较低,可以使用双检查机制

   static MyClass* getInstance(){
        if (m_instance == nullptr) {
        	std::unique_lock locker(m_mutex);
        	if (m_instance == nullptr) {
            	m_instance = new MyClass();            
        	}
         }
        return m_instance;
    }

这样处理的原因: m_instance != nullptr时表示一定被new过了; m_instance == null,表示可能被new 过,也可能没有被new过。

2. 饿汉式

饿汉式单例代码如下,线程安全的,但是不能手动灵活释放对象

    static MyClass* getInstance(){
        static MyClass c;
        return &c;
    }

3. std::call_once

        std::call_once 是C++11新增功能,能够保证在多线程情况下指定函数只被调用一次;具备互斥量的能力,且比互斥量消耗的资源更少;需要与一个标记结合使用std::once_flag。

class MyClass{
    public:        
    static MyClass* getInstance(){
        std::call_once(flag, [&](){
            m_instace = new MyClass;
        })
        return m_instance;
    }
    static void destory(){
        if (m_instance) {
            delete m_instance;
            m_instance = nullptr;
        }
    }
    
    private:
        static MyClass* m_instance;
        static std::once_flag  flag;
}
MyClass* MyClass::m_instance = nullptr; //定义在.cpp中
std::once_flag MyClass::flag;

        缺点是:不能实现多次创建、销毁单例, 调用 getInstance()、destory(),成功创建和销毁一次单例,再次调用 getInstance()、destory() 失败。

总结:

综合对懒汉式、饿汉式、call_once的使用方式,个人认为使用懒汉式+锁+destory的方式能解决内存泄露问题、灵活多次创建和销毁、同时也保证了线程安全。

你可能感兴趣的:(C/C++,c++,单例模式)