单例模式
1、只能创建出一个对象的类,这种类就叫作单例类,这种模式就叫作单例模式。
2、为什么需要单例模式,是为了提高安全性和稳定性的技巧。
只允许存在唯一对象实例
单例模式的商业应用:
网站计数器
日志管理系统
连接池、线程池、内存池
3、获取对象实例的专门方法
a、全局变量的定义不受控制,能防君子不能防小人
b、专门方法是类的一部分,"我是类型我做主",
借助类禁止在外部创建对象,仅在类内部提供获取对象的接口。
4、如何实现单例模式
a、禁止在类外部创建实例,私有所有的构造函数 private
b、类自己维护其唯一实例,
静态成员变量 static 类名 instance;
静态成员指针 static 类名* instance;
c、提供访问该实例的方法,静态成员函数getInstance()
5、饿汉单例模式
不管是否需要对象都已经创建好了。
优点:效率高、速度快、稳定。
缺点:浪费资源,不管需不需要对象都已经创建好;
6、懒汉单例模式
当首次使用获取对象时才会真正创建出对象。
优点:节约资源
缺点:效率低,速度慢,不安全(多线程情况下)。
下面我们按照第四点来实现一个单例模式:
#include<iostream>
using namespace std;
class Student
{
private:
//static Student instance;
Student(){}
Student(const Student & that)
{
cout<<"I'm copy"<<endl;
}
Student& operator =(const Student& that)
{
cout<<"我是赋值构造函数!"<<endl;
}
public:
static Student& getobj(void)
{
static Student obj;
return obj;
}
};
int main()
{
Student& stu1=Student::getobj();
cout<<&stu1<<endl;
Student& stu2=Student::getobj();
cout<<&stu2<<endl;
}
这个例子:我们分别用stu1和stu2得到两个对象,按照理解判断应该是两个对象,但把对象的地址打印出来我们发现,是同一个地址,这就说明,这是同一个对象,所以这个类只能创建一个对象,所以是单例模式。
饿汉模式的单例:
#include<iostream>
using namespace std;
class Student
{
private:
static Student obj;
Student(){}
Student(const Student & that)
{
cout<<"I'm copy"<<endl;
}
Student& operator =(const Student& that)
{
cout<<"我是赋值构造函数!"<<endl;
}
public:
static Student& getobj(void)
{
return obj;
}
};
Student Student::obj;
int main()
{
Student& stu1=Student::getobj();
cout<<&stu1<<endl;
Student& stu2=Student::getobj();
cout<<&stu2<<endl;
}
注意:我们在类中用static声明了一给成员,那么这个成员只能在类外定义也就是Student Student::obj;这里的obj是先创建了,无论是不是用,所以称为饿汉模式。
再来看一下懒汉模式:
#include<iostream>
using namespace std;
class Student
{
private:
static Student* obj;
Student(){}
Student(const Student & that)
{
cout<<"I'm copy"<<endl;
}
Student& operator =(const Student& that)
{
cout<<"我是赋值构造函数!"<<endl;
}
public:
static Student& getobj(void)
{
if(Student::obj==NULL)
{
obj=new Student;
return *obj;
}
}
};
Student* Student::obj;
int main()
{
Student& stu1=Student::getobj();
cout<<&stu1<<endl;
Student& stu2=Student::getobj();
cout<<&stu2<<endl;
}
这个是懒汉模式,运行结果也是同样的对象地址,注意,我没有写析构函数释放资源,稍微有点不合理,大家可以自己试着加一下,new最好要配合delete使用。
考虑到线程安全,做出扩展
class Lock
{
private:
CCriticalSection m_cs;
public:
Lock(CCriticalSection cs) : m_cs(cs)
{
m_cs.Lock();
}
~Lock()
{
m_cs.Unlock();
}
};
class Singleton
{
private:
Singleton();
Singleton(const Singleton &);
Singleton& operator = (const Singleton &);
public:
static Singleton *Instantialize();
static Singleton *pInstance;
static CCriticalSection cs;
};
Singleton* Singleton::pInstance = 0;
Singleton* Singleton::Instantialize()
{
if(pInstance == NULL)
{ //double check
Lock lock(cs); //用lock实现线程安全,用资源管理类,实现异常安全
//使用资源管理类,在抛出异常的时候,资源管理类对象会被析构,析构总是发生的无论是因为异常抛出还是语句块结束。
if(pInstance == NULL)
{
pInstance = new Singleton();
}
}
return pInstance;
}