单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
C++单例模式有许多种实现方法,在C++中,甚至可以直接用一个全局变量做到这一点,但这样的代码显得很不优雅。《设计模式》一书中给出了一种很不错的实现,定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有静态方法获取该实例。
如下:单例模式程序一:
- Singleton.h文件:
- class CSingleTon
- {
- private:
- CSingleTon(); //构造函数为私有的
- public:
- ~CSingleTon();//析构函数是公有的
- public:
- static CSingleTon* GetInstance();//用来获取实例,公有
- int GetVar(); //获取m_num的值
- void SetVar(int var); //设置m_num的值
- void Print(); //在控制台上打印m_num的值
- private:
- static CSingleTon *m_pInstance; //类的唯一实例,私有
- int m_var;
- };
- Singleton.cpp
- #include <iostream>
- using namespace std;
- #include "singleton.h"
- CSingleTon *CSingleTon::m_pInstance = CSingleTon::GetInstance();//静态成员变量的初始化
- CSingleTon::CSingleTon()
- {
- m_var = 0;
- }
- CSingleTon::~CSingleTon()
- {
- if(m_pInstance != NULL)
- delete m_pInstance;
- }
- void CSingleTon::SetVar(int var)
- {
- m_var = var;
- }
- int CSingleTon::GetVar()
- {
- return m_var;
- }
- CSingleTon* CSingleTon::GetInstance()
- {
- if(m_pInstance == NULL)
- {
- m_pInstance = new CSingleTon();
- }
- return m_pInstance;
- }
- void CSingleTon::Print()
- {
- cout<<m_var<<endl;
- }
- Main.cpp
- #include <iostream>
- using namespace std;
- #include "singleton.h"
- int main(void)
- {
- CSingleTon *tonfir = CSingleTon::GetInstance();
- CSingleTon *tonsec = CSingleTon::GetInstance();
- printf("%p\n%p\n", tonfir, tonsec); //打印两个单例类对象指针的地址
- tonfir->Print();
- tonfir->SetVar(5);
- tonsec->Print();
- delete tonfir; //调用析构函数
- delete tonsec; //其实这句话可以不写,因为tonfir和tonsec指向的是同一个对象
- return 0;
- }
该单例类的特征:
1.它有一个唯一实例的静态指针变量m_pInstance,并且是私有的;
2.它有一个公有函数,用于获取这个唯一实例,并在需要的时候创建该实例;
3.它的构造函数是私有的,这样就不能在别处创建该类的实例
大多时候,这样的实现都不会出现问题。有经验的读者可能会问,m_pInstance指向的空间什么时候释放呢?更严重的问题是,这个实例的析构操作什么时候执行?如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面所示的代码无法实现这个要求。我们需要一种方法,正常地删除该实例。
可以在程序结束时调用GetInstance并对返回的指针调用delete操作。这样做可以实现功能,但是不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。
一个妥善的方法是让这个类自己知道在合适的时候把自己删除。或者说把删除自己的操作挂在系统中的某个合适的点上,使其在恰当的时候自动被执行。
我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在C++单例模式类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人)
- Singleton.h
- #pragma once
- class CSingleTon
- {
- private:
- CSingleTon(); //构造函数为私有的
- public: //析构函数要是公有的
- ~CSingleTon();
- private:
- static CSingleTon *m_pInstance; //类的唯一实例
- class CGarbo //内部类,该类的唯一工作是在析构函数中删除CSingleTon的实例
- {
- public:
- ~CGarbo()
- {
- // if(CSingleTon::m_pInstance) // 内部类不能去访问包含其类的非公有成员变量
- // delete CSingleTon::m_pInstance;
- if(CSingleTon::GetInstance())
- delete CSingleTon::GetInstance();
- }
- };
- static CGarbo Garbo; //定义一静态成员,在程序结束时,系统会调用它的析构函数
- protected:
- int m_num;
- public:
- static CSingleTon* GetInstance(); //用来获取实例
- void SetNum(int num); //设置m_num的值
- int GetNum(); //获取m_num的值
- void PrintNum(); //在控制台上打印m_num的值
- };
- Singleton.cpp
- #include "singleton.h"
- #include <iostream>
- using namespace std;
- CSingleTon *CSingleTon::m_pInstance = CSingleTon::GetInstance();//静态成员变量的初始化
- CSingleTon::CSingleTon()
- {
- cout<<"Construct"<<endl;
- this->m_num = 0;
- }
- CSingleTon::~CSingleTon()
- {
- cout<<"Destruct"<<endl;
- if(m_pInstance != NULL)
- delete m_pInstance;
- }
- CSingleTon* CSingleTon::GetInstance()
- {
- if(m_pInstance == NULL)
- {
- m_pInstance = new CSingleTon();
- }
- return m_pInstance;
- }
- void CSingleTon::SetNum(int num)
- {
- this->m_num = num;
- }
- int CSingleTon::GetNum()
- {
- return this->m_num;
- }
- void CSingleTon::PrintNum()
- {
- cout<<this->m_num<<endl;
- }
- Main.cpp
- #include <iostream>
- using namespace std;
- #include "singleton.h"
- int main(void)
- {
- CSingleTon *tonfir = CSingleTon::GetInstance();
- CSingleTon *tonsec = CSingleTon::GetInstance();
- tonfir->PrintNum();
- tonsec->PrintNum();
- tonfir->SetNum(5);
- tonsec->PrintNum();
- return 0;
- }
原理:
程序运行结束时,系统自动析构所有的全局变量。也会析构所有类的静态成员变量。
在程序运行结束时,系统会调用CSingleTon的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。
使用这种方法释放单例对象有以下特征:
1.在单例类内部定义专有的嵌套类
2.在单例类内部定义私有的专门用于释放单例对象的静态成员
3.利用程序在结束时,析构静态变量的特性,选择最终的释放时机
4.使用C++单例模式的代码不需要任何操作,不必关心对象的释放