昨天晚上跟同学讨论问题的时候讨论到了设计模式,发现我对此了解的并不是很多。今天开始就陆陆续续的开始记录一些我的学习过程。
这篇博客就先从设计模式的单例模式说起,自己动手写了一个用c++实现的单例模式,略抒薄见,望大神不吝赐教!
个人对单例模式是其主要应用在系统或者工程中只需要创建一个实例,且此实例可以在整个系统中被访问的情况,比如一台PC只能外接一个键盘。那么用c++如何实现呢?我首先想到了使用一个全局变量来实现(略粗暴),他可以实现在整个系统中均可见,但是全局变量有个问题是不能保证只声明一个实例,且当系统中全局变量比较多的时候,使用起来就会很混乱。
因此,考虑使用类来实现,将数据封装在一个特殊的类中,然后这个类严格管理它的数据的唯一性,不允许程序员随意创建这个类的对象实例。那么如何保证类只会创建一个实例呢?我们很容易想到类的私有成员函数,只有类对象才可以调用它!对,我们就将构造函数声明为私有的,这样的话就可以保证类外不会随意创建对象了。那么接下来的问题是,私有成员函数只有类的对象或者成员函数才能访问,我们怎么来调用这个私有的构造函数呢?使用对象调用肯定是不可能了,因为在需要调用私有构造函数之前,还没有对象存在,那我们就考虑用成员函数来实现。一般的成员函数都是跟类对象绑定的,也需要对象来主动调用,只有static型的成员函数最特殊!它不与任何对象绑定,独立于class对象之外,不必产生对象也可以使用它们。同时,将成员变量uInstance声明为static的,也就是静态的,所以只会在Singleton这个类在被程序加载时执行,也就是这条语句只在最初的一次执行,之后就不再执行了。因为只执行了一次,所以可以保证程序中所拥有的Singleton实例只有一个,也就是不会再new一个新的实例。因此,我们通过声明一个公有的static类型的函数来调用私有构造函数,实现对象实例的创建。初步的代码如下:
class Singleton{
private:
Singleton() {} //构造函数私有,实现只能创建一个对象
static Singleton* uInstance;
public:
static Singleton* get(); //static公有成员函数独立于class对象之外,不必产生对象也可以使用它们。
};
Singleton* Singleton::get(){
if(uInstance == NULL){ //判断是否创建过对象
uInstance = new Singleton;
}
return uInstance;
}
现在基本实现了一个初步的单例模式,使用get()调用了构造函数,用new运算符在堆上创建了一个对象( 构造函数私有化的类的设计可以保证只能用new命令在堆中来生成对象,只能动态的去创建对象,这样可以自由的控制对象的生命周期)。但是有个问题是,在堆上创建的对象需要程序员手动析构,上述代码没有声明析构函数,在需要释放创建的对象时,系统也并不会自动调用默认析构函数(只有在栈上创建的对象才会自动调用析构函数),编译会出错。此时,我需要自己声明一个析构函数了,我把析构函数声明为私有的,再声明一个destroy函数,通过delete隐式调用析构函数就可以了。代码如下:
#include <iostream>
using namespace std;
class Singleton{
private:
Singleton() {} //构造函数私有,实现只能创建一个对象
Singleton (Singleton const&) {}; // 隐藏拷贝构造函数
Singleton & operator=( Singleton const&) {}; // 隐藏赋值运算符
~Singleton();
static Singleton* uInstance;
public:
static Singleton* get(); //static公有成员函数独立于class对象之外,不必产生对象也可以使用它们
void Destroy();
};
Singleton* Singleton::uInstance = NULL; //初始化静态变量
Singleton::~Singleton(){
if(uInstance == NULL){
return;
}
cout<<"析构"<<endl;
}
void Singleton:: Destroy() {
delete this;
cout<<"删除完成!"<<endl;
}
Singleton* Singleton::get(){
if(uInstance == NULL){
uInstance = new Singleton;
}
return uInstance;
}
int main()
{
Singleton * instance=Singleton::get();
instance->Destroy();
return 0;
}
运行结果如下:
注意到类中我还声明了static类型的拷贝构造函数和赋值运算符,目的是防止在后续的程序操作中无意间创建新对象,破坏实例唯一性。
今天的这篇文章只是针对单线程中的单例模式,涉及到多线程的单例模式还需要设计锁来防止死锁得情况的发生。后续我会继续学习并更新!在此,感谢FD同学给我的帮助和指正!