- 为什么使用单例模式
大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。比如使用Socket时,只需要一个Socket对象即可,这时候单例模式就有实战之地了。
实现单例步骤常用步骤
- 构造函数私有化
提供一个全局的静态方法(全局访问点)
在类中定义一个静态指针,指向本类的变量的静态变量指针
单例模式分类
懒汉式、饿汉式
- 懒汉式单例模式普通写法 单线程下
Singelton.h #include <iostream> using namespace std; class Singelton { private: Singelton(); public: virtual ~Singelton(); public: static Singelton* getInstance(); void freeInstance(); static void printT(); private: static Singelton *m_Singleton; static int m_count; }; Singelton* Singelton::m_Singleton = nullptr; int Singelton::m_count = 0; ---------------------------------------------------------- Singelton.cpp #include "Singelton.h" Singelton::Singelton() { m_Singleton = nullptr; m_count = 0; } Singelton::~Singelton() { } /**只有当使用的时候才会将对象创建出来**/ Singelton* Singelton::getInstance() { /*单线程下该写法没有问题,多线程有问题*/ if (m_Singleton == nullptr) { m_Singleton = new Singelton(); } return m_Singleton; } void Singelton::freeInstance() { if (m_Singleton != nullptr) { delete m_Singleton; m_Singleton = nullptr; m_count = 0; } } void Singelton::printT() { cout << "m_count: " << m_count << endl; } main.cpp #include"Singelton.h" int main() { Singelton* p1 = Singelton::getInstance(); Singelton* p2 = Singelton::getInstance(); if (p1 != p2) { cout << "不是同一个对象" << endl; } else { cout << "是同一个对象" << endl; } p1->printT(); p2->printT(); system("pause"); Singelton::freeInstance(); return 0; }
- 懒汉式单例模式多线程下写法
异同点:单线程的写法使用到多线程后,每次调用GetInstance()静态方法时,必须判断 m_Singleton == nullptr,使程序相对开销增大。在多线程中,该写法会导致多个实例的产生,从而导致运行代码不正确以及内存的泄露。
原因:因为C++中构造函数并不是线程安全的 ,C++中的构造函数简单来说分两步:内存分配 、初始化成员变量。由于多线程的关系,可能当我们在分配内存好了以后,还没来得急初始化成员变量,就进行线程切换,另外一个线程拿到所有权后,由于内存已经分配了,但是变量初始化还没进行,因此打印成员变量的相关值会发生不一致现象。
但是当使用懒汉式处理大数据时,此处的锁却变成了影响性能的关键因素。
//多线程优化单例模式 class Singelton { private: Singelton(); //防止拷贝构造和赋值操作 Singelton(const Singelton&) { ; } Singelton& operator=(const Singelton &obj) { ; } public: virtual ~Singelton(); public: static Singelton* getInstance(); static void freeInstance(); void printT(); private: static Singelton *m_Singleton; static std::mutex m_mutex; static int m_count; }; ----------------------------------------------------------- Singelton* Singelton::m_Singleton = nullptr; int Singelton::m_count = 0; std::mutex Singelton::m_mutex; Singelton::Singelton() { m_Singleton = nullptr; m_count = 0; } Singelton::~Singelton() { } /**只有当使用的时候才会将对象创建出来**/ Singelton* Singelton::getInstance() { if (m_Singleton == nullptr) //double check { //只有当m_Singleton等于null时,才开始使用加锁机制 二次检查 m_mutex.try_lock(); if (m_Singleton == nullptr) { m_Singleton = new Singelton(); } m_mutex.unlock(); } return m_Singleton; } void Singelton::freeInstance() { if (m_Singleton != nullptr) { delete m_Singleton; m_Singleton = nullptr; m_count = 0; } } void Singelton::printT() { std::cout << "m_count: " << m_count << std::endl; }
了解了懒汉式单例模式的创建方式后,再来看饿汉式对比度就显而易见了,,饿汉式就是不管有没有人要这个实例对象,都先把实例对象创建出来。
//饿汉式单例模式 class Singelton { private: Singelton(); //防止拷贝构造和赋值操作 Singelton(const Singelton&) { ; } Singelton& operator=(const Singelton &obj) { ; } public: virtual ~Singelton(); public: static Singelton* getInstance(); static void freeInstance(); private: static Singelton *m_Singleton; }; Singelton* Singelton::m_Singleton = new Singelton(); Singelton::Singelton() { } Singelton::~Singelton() { } /**只有当使用的时候才会将对象创建出来**/ Singelton* Singelton::getInstance() { return m_Singleton; } void Singelton::freeInstance() { if (m_Singleton != nullptr) { delete m_Singleton; m_Singleton = nullptr; } }