赞
踩
本文对 C++ 的单例模式进行简单介绍和实现。
参考:
- C++ 线程安全的单例模式总结(强烈建议阅读原文,本文相当于做了总结,留作学习,并添加了一种新的单例方法 std::call_once )
单例模式是指在整个系统生命周期内,保证一个类只能产生一个实例,确保该类的唯一性。
两个原因:
但是在实现单例模式时,需要考虑到线程安全的问题。
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
单例模式可以分为 懒汉式 和 饿汉式 ,两者之间的区别在于创建实例的时间不同。
系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。这种方式要考虑线程安全。
系统一运行,就初始化创建实例,当需要时,直接调用即可。这种方式本身就线程安全,没有多线程的线程安全问题。
单例的经典实现方式是「静态局部变量的懒汉单例」,推荐使用这种方式。
这种情况是线程不安全的,不作详细介绍。
使用互斥锁保证线程安全。
头文件:
/// 加锁的懒汉式实现 // class SingleInstance { public: // 获取单实例对象 static SingleInstance *GetInstance(); //释放单实例,进程退出时调用 static void deleteInstance(); // 打印实例地址 void Print(); private: // 将其构造和析构成为私有的, 禁止外部构造和析构 SingleInstance(); ~SingleInstance(); // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值 SingleInstance(const SingleInstance &signal); const SingleInstance &operator=(const SingleInstance &signal); private: // 唯一单实例对象指针 static SingleInstance *m_SingleInstance; static std::mutex m_Mutex; };
源文件:
//初始化静态成员变量 SingleInstance *SingleInstance::m_SingleInstance = nullptr; std::mutex SingleInstance::m_Mutex; // 注意:不能返回指针的引用,否则存在外部被修改的风险! SingleInstance * SingleInstance::GetInstance() { // 这里使用了两个 if 判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁, // 避免每次调用 GetInstance的方法都加锁,锁的开销毕竟还是有点大的。 if (m_SingleInstance == nullptr) { std::unique_lock<std::mutex> lock(m_Mutex); // 加锁 if (m_SingleInstance == nullptr) { volatile auto temp = new (std::nothrow) SingleInstance(); m_SingleInstance = temp; } } return m_SingleInstance; } void SingleInstance::deleteInstance() { std::unique_lock<std::mutex> lock(m_Mutex); // 加锁 if (m_SingleInstance) { delete m_SingleInstance; m_SingleInstance = nullptr; } } void SingleInstance::Print() { std::cout << "我的实例内存地址是:" << this << std::endl; } SingleInstance::SingleInstance() { std::cout << "构造函数" << std::endl; } SingleInstance::~SingleInstance() { std::cout << "析构函数" << std::endl; }
#include <iostream> #include <memory> #include <mutex> class Singleton { public: static std::shared_ptr<Singleton> getSingleton(); void print() { std::cout << "Hello World." << std::endl; } ~Singleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } private: Singleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; static std::shared_ptr<Singleton> singleton = nullptr; static std::mutex singletonMutex; std::shared_ptr<Singleton> Singleton::getSingleton() { if (singleton == nullptr) { std::unique_lock<std::mutex> lock(singletonMutex); if (singleton == nullptr) { volatile auto temp = std::shared_ptr<Singleton>(new Singleton()); singleton = temp; } } return singleton; }
头文件:
/// 内部静态变量的懒汉实现 // class Single { public: // 获取单实例对象 static Single& GetInstance(); // 打印实例地址 void Print(); private: // 禁止外部构造 Single(); // 禁止外部析构 ~Single(); // 禁止外部拷贝构造 Single(const Single &single) = delete; // 禁止外部赋值操作 const Single &operator=(const Single &single) = delete; };
源文件:
Single& Single::GetInstance() { /** * 局部静态特性的方式实现单实例。 * 静态局部变量只在当前函数内有效,其他函数无法访问。 * 静态局部变量只在第一次被调用的时候初始化,也存储在静态存储区,生命周期从第一次被初始化起至程序结束止。 */ static Single single; return single; } void Single::Print() { std::cout << "我的实例内存地址是:" << this << std::endl; } Single::Single() { std::cout << "构造函数" << std::endl; } Single::~Single() { std::cout << "析构函数" << std::endl; }
但是,这种方法也有点问题:在多线程场景下还是有可能会存在线程安全的问题,因为多线程同时调用 GetInstance() 方法有可能还是会产生竞争。
解决这个问题的一种做法是:在程序的单线程启动阶段就调用 GetInstance() 方法。
经过大佬指正,上面这种说法是不正确的,在 C++11 中,静态局部变量这种方式天然是线程安全的,不存在线程安全的问题。详见 stack overflow :Singleton instance declared as static variable of GetInstance method, is it thread-safe? 。
头文件:
// 饿汉实现 / class Singleton { public: // 获取单实例 static Singleton* GetInstance(); // 释放单实例,进程退出时调用 static void deleteInstance(); // 打印实例地址 void Print(); private: // 将其构造和析构成为私有的, 禁止外部构造和析构 Singleton(); ~Singleton(); // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值 Singleton(const Singleton &signal); const Singleton &operator=(const Singleton &signal); private: // 唯一单实例对象指针 static Singleton *g_pSingleton; };
源文件:
// 代码一运行就初始化创建实例 ,本身就线程安全 Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton(); Singleton* Singleton::GetInstance() { return g_pSingleton; } void Singleton::deleteInstance() { if (g_pSingleton) { delete g_pSingleton; g_pSingleton = nullptr; } } void Singleton::Print() { std::cout << "我的实例内存地址是:" << this << std::endl; } Singleton::Singleton() { std::cout << "构造函数" << std::endl; } Singleton::~Singleton() { std::cout << "析构函数" << std::endl; }
#include <iostream> #include <memory> #include <mutex> class Singleton { public: static std::shared_ptr<Singleton> getSingleton(); void print() { std::cout << "Hello World." << std::endl; } ~Singleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } private: Singleton() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; static std::shared_ptr<Singleton> singleton = nullptr; static std::once_flag singletonFlag; std::shared_ptr<Singleton> Singleton::getSingleton() { std::call_once(singletonFlag, [&] { singleton = std::shared_ptr<Singleton>(new Singleton()); }); return singleton; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。