一、什么是单例模式
1.1单例模式的基本概念
单例模式属于创建型模式,这种模式涉及到一种单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式。
单例类的特征:1.单例类最多只能有一个实例;2.单例类必须自己创建自己唯一的实例;3.单例类必须给所有其他的对象提供这一实例。
1.2使用单例模式的好处
使用单例类是为了保证某一个类仅有一个实例,并提供一个访问它的全局访问点。
单例类主要解决了一个全局使用的类的频繁的创建与销毁。
所以单例模式有以下几个优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例;2、避免对资源的多重占用。
那单例模式有一个不好的地方就是,单例类没有接口,不能继承。
1.3单例模式的使用场景
当我们想在程序中控制实例数目,节约系统资源的时候,可以考虑使用单例类。
>>1.Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
>>2.一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
二、单例模式的C++实现
2.1懒汉式
什么叫懒汉式的单例类呢?顾名思义,懒汉很懒,只有他饿了的时候才会去找吃的,也就是说只有在需要的时候才会去实例化。一次都没有需要的时候,不会去构造这个对象,这个程序中也就没有这个对象的实例,只有需要了一次了,实例化了一次,这个对象才会一直存在。
//Singleton.cpp
#include<iostream>
#include<mutex>
using namespace std;
//mutex singletonLock;
class Singleton {
public:
static Singleton* getInstance() {
static Singleton* singeltonInstance;//函数内的静态变量,声明加定义,已分配空间。
cout << "getInstance in singleton." << endl;
//线程安全
//if(singeltonInstance == NULL){
// singletonLock.lock();//线程安全
if (singeltonInstance == NULL)
singeltonInstance = new Singleton();
// singletonLock.unlock();
//}
return singeltonInstance;
};
private:
//static Singleton* singeltonInstance;//若在此处定义,类内的静态成员变量,要记得在类外部的cpp文件的全局进行初始化。(仅仅是声明,并没定义,所以没有分配空间)
//Static members obey the usual class member access rules (clause 11). When used in the declaration of a class member, the static specifier shall only be used in the member declarations that appear within the member-specification of the class declaration. [Note: it cannot be specified in memberdeclarations that appear in namespace scope. ]
Singleton() {
cout << "Construct in singleton." << endl;
}
Singleton(const Singleton&);//拷贝构造函数和赋值运算符也是私有的,以禁止拷贝和赋值;
Singleton& operator=(const Singleton&);
};
懒汉式的单例类在多线程的使用中存在线程安全问题。如果有两个线程同时获取单例类的实例,又都发现实例不存在,因此都会进行实例化,会产生两个实例都要赋给singeltonInstance,会造成严重的错误,要解决这个问题就要加锁。上面程序注释掉的为线程安全的部分,即为懒汉式单例模式线程安全问题的一个解决方案。
2.2饿汉式
饿汉式的单例类,会在全局作用域进行单例类的实例化,并以此来初始化。
//Singleton.cpp
#include<iostream>
using namespace std;
class Singleton;
class Singleton {
private:
Singleton() {};//构造函数私有化
Singleton(Singleton &s) {}; //拷贝构造私有化
Singleton& operator=(const Singleton& s) {};
static Singleton* singletonInstance;
public:
static Singleton* getInstance() {
return singletonInstance;
}
};
//......
Singleton* Singleton::singletonInstance = new Singleton();//全局实例化
2.3 单例模式的GC
1.单例模式中实例化的对象是new出来的,需要对它负责,也就是需要delete;
2.delete一个对象时,会调用它的析构函数;
3.如果在析构函数里使用delete进行内存管理,由于对象是new出来的,根本进不去析构函数,无法自动的销毁;
4.如果在析构函数里使用delete进行内存管理,就一定会造成一个析构的循环的(见2)。
那么应该怎样在C++的程序中显式的进行单例对象的内存管理呢?一个比较好的方法是定义一个内部垃圾回收类,并且在Singleton中定义一个此类的静态成员。程序结束时,系统会自动析构此静态成员,此时,在此类的析构函数中析构Singleton实例,就可以实现singletonInstance的自动释放。
//Singleton.cpp
#include<iostream>
using namespace std;
class Singleton;
class Singleton {
private:
Singleton() { cout << "Construct singleton." << endl; };//构造函数私有化
Singleton(Singleton &s) {}; //拷贝构造私有化
Singleton& operator=(const Singleton& s) {};
static Singleton* singletonInstance;
class SingletonCG {//单例的回收
public:
~SingletonCG(){
if (singletonInstance != NULL)
delete singletonInstance;
singletonInstance = NULL;
cout << "Singleton delete." << endl;
system("pause");
}
};
static SingletonCG cg;
public:
static Singleton* getInstance() {
cout << "getInstance." << endl;
return singletonInstance;
}
};
//......
Singleton* Singleton::singletonInstance = new Singleton();//全局实例化
Singleton::SingletonCG Singleton::cg;////类的静态成员需要类外部初始化,这一点很重要,否则程序运行连GC的构造都不会进入,何谈自动析构