设计模式(2)-单例模式(Singleton)
(3) static与单例模式
由于static数据成员存储的位置是固定不变的,实质上,利用指针访问同一静态数据成员的不同实例,是访问同一内存地址。也就是说,访问同一静态数据成员的不同实例,实际上,访问的是同一实例(单例)。
main.cpp下述代码示例,s1、s2、s3,s4,s5是不同实例,分别new了不同的内存。但修改s1->test、s4->test,同时修改了s3,s4,s5三个实例中静态test值。实质上,s1->test、s2->test、s3->test、s4->test、s5->test是“同一”实例。
[html]
//Test2
qDebug()<<s1->test;//s1->test = 1
s2->test++;
qDebug()<<s1->test;//s1->test = 2
qDebug()<<s2->test;//s2->test = 2
Singleton *s4 = new Singleton;
qDebug()<<s4->test;//s4->test = 1
Singleton *s5 = new Singleton;
qDebug()<<s5->test;//s5->test = 1
s1->test++;
s4->test++;
qDebug()<<s1->test;//s1->test = 3
qDebug()<<s2->test;//s2->test = 3
qDebug()<<s3->test;//s3->test = 3
qDebug()<<s4->test;//s4->test = 3
qDebug()<<s5->test;//s5->test = 3
//Test2
qDebug()<<s1->test;//s1->test = 1
s2->test++;
qDebug()<<s1->test;//s1->test = 2
qDebug()<<s2->test;//s2->test = 2
Singleton *s4 = new Singleton;
qDebug()<<s4->test;//s4->test = 1
Singleton *s5 = new Singleton;
qDebug()<<s5->test;//s5->test = 1
s1->test++;
s4->test++;
qDebug()<<s1->test;//s1->test = 3
qDebug()<<s2->test;//s2->test = 3
qDebug()<<s3->test;//s3->test = 3
qDebug()<<s4->test;//s4->test = 3
qDebug()<<s5->test;//s5->test = 3
【UML图】
图1 单例模式UML
1 Singleton应用了单例模式,定义了一个类型为Singleton的私有静态的_instance,一个类型为int的公有的_test,以及一个类型为QString的_name对象。
2 运用静态方法createInstance创建实例。
3 定义了两个公有方法setName()、getName()。
【示例代码】
singleton.h
[html] view plaincopyprint?#ifndef SINGLETON_H
#define SINGLETON_H
#include <QString>
class Singleton
{
public:
Singleton();
private:
static Singleton* _instance;
QString _name;
public:
static int test;
public:
static Singleton *createInstance();
void setName(QString name);
QString getName();
};
#endif // SINGLETON_H
#ifndef SINGLETON_H
#define SINGLETON_H
#include <QString>
class Singleton
{
public:
Singleton();
private:
static Singleton* _instance;
QString _name;
public:
static int test;
public:
static Singleton *createInstance();
void setName(QString name);
QString getName();
};
#endif // SINGLETON_H
singleton.cpp
[html] view plaincopyprint?#include <QDebug>
#include "singleton.h"
Singleton* Singleton::_instance=NULL;
int Singleton::test = 0;
Singleton::Singleton()
{
qDebug()<<"construct";
test = 1;
}
Singleton* Singleton::createInstance()
{
if(_instance == NULL)
{
_instance = new Singleton;
}
return _instance;
}
void Singleton::setName(QString name)
{
_name = name;
}
QString Singleton::getName()
{
return _name;
}
#include <QDebug>
#include "singleton.h"
Singleton* Singleton::_instance=NULL;
int Singleton::test = 0;
Singleton::Singleton()
{
qDebug()<<"construct";
test = 1;
}
Singleton* Singleton::createInstance()
{
if(_instance == NULL)
{
_instance = new Singleton;
}
return _instance;
}
void Singleton::setName(QString name)
{
_name = name;
}
QString Singleton::getName()
{
return _name;
}
main.cpp
[html] view plaincopyprint?#include <QDebug>
#include "singleton.h"
int main(void)
{
Singleton *s1 = Singleton::createInstance();
Singleton *s2 = Singleton::createInstance();
if(s1 == s2)
{
qDebug()<<"s1 , s2 are the same instance";
}
s1->setName("zhangsan");
qDebug()<<s1->getName();
qDebug()<<s2->getName();
s2->setName("lisi");
qDebug()<<s1->getName();
qDebug()<<s2->getName();
//Test1
int i;
i = s1->test;
i = Singleton::test;
s1->createInstance();
Singleton *s3 = new Singleton;
i = s3->test;
//Test2
qDebug()<<s1->test;//s1->test = 1
s2->test++;
qDebug()<<s1->test;//s1->test = 2
qDebug()<<s2->test;//s2->test = 2
Singleton *s4 = new Singleton;
qDebug()<<s4->test;//s4->test = 1
Singleton *s5 = new Singleton;
qDebug()<<s5->test;//s5->test = 1
s1->test++;
s4->test++;
qDebug()<<s1->test;//s1->test = 3
qDebug()<<s2->test;//s2->test = 3
qDebug()<<s3->test;//s3->test = 3
qDebug()<<s4->test;//s4->test = 3
qDebug()<<s5->test;//s5->test = 3
return 0;
}
#include <QDebug>
#include "singleton.h"
int main(void)
{
Singleton *s1 = Singleton::createInstance();
Singleton *s2 = Singleton::createInstance();
if(s1 == s2)
{
qDebug()<<"s1 , s2 are the same instance";
}
s1->setName("zhangsan");
qDebug()<<s1->getName();
qDebug()<<s2->getName();
s2->setName("lisi");
qDebug()<<s1->getName();
qDebug()<<s2->getName();
//Test1
int i;
i = s1->test;
i = Singleton::test;
s1->createInstance();
Singleton *s3 = new Singleton;
i = s3->test;
//Test2
qDebug()<<s1->test;//s1->test = 1
s2->test++;
qDebug()<<s1->test;//s1->test = 2
qDebug()<<s2->test;//s2->test = 2
Singleton *s4 = new Singleton;
qDebug()<<s4->test;//s4->test = 1
Singleton *s5 = new Singleton;
qDebug()<<s5->test;//s5->test = 1
s1->test++;
s4->test++;
qDebug()<<s1->test;//s1->test = 3
qDebug()<<s2->test;//s2->test = 3
qDebug()<<s3->test;//s3->test = 3
qDebug()<<s4->test;//s4->test = 3
qDebug()<<s5->test;//s5->test = 3
return 0;
}
【运行结果】
[html] view plaincopyprint?construct
s1 , s2 are the same instance
"zhangsan"
"zhangsan"
"lisi"
"lisi"
construct
1
2
2
construct
1
construct
1
3
3
3
3
3
construct
s1 , s2 are the same instance
"zhangsan"
"zhangsan"
"lisi"
"lisi"
construct
1
2
2
construct
1
construct
1
3
3
3
3
3
s1、s2是同一实例,调用s2->setName修改s2的同时,修改了s1
【实例剖析】
实例1
QT中对SQL进行相关操作,就是采用单例模式。以sqlite3数据库进行说明。
1 建立连接时,调用类似下述代码:
[html] view plaincopyprint?bool initSql::createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(dbName);
if (!db.open())
{
QMessageBox::warning(0, QObject::tr("Database Error"),db.lastError().text());
return false;
}
return true;
}
bool initSql::createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(dbName);
if (!db.open())
{
QMessageBox::warning(0, QObject::tr("Database Error"),db.lastError().text());
return false;
}
return true;
}
2 断开连接时,调用:
[html] view plaincopyprint?void initSql::closeConnection()
{
QString name;
{
name = QSqlDatabase::database().connectionName();
}
QSqlDatabase::database().close();
QSqlDatabase::removeDatabase(name);
}
void initSql::closeConnection()
{
QString name;
{
name = QSqlDatabase::database().connectionName();
}
QSqlDatabase::database().close();
QSqlDatabase::removeDatabase(name);
}
这样做,好处是,只需要连接数据库一次,得到静态的实例,就可以在工程任何地方,通过该实例,对数据库进行操作,而不必连接第二次。缺陷是,不能同时创建一个以上的实例。
其实,单例模式,优点和缺陷都是由于只能创建一个实例引起的。
优点容易理解,讲讲缺点。
运用sqlite3建立了一个数据库,现在编程对数据库进行访问。现在的问题是,有可能会出现这样的情形,在同一时刻,网页cgi程序和QT编写的程序要同时对数据库访问。cgi没有采用单例模式,而Qt采用单例模式。CGI操作sqlite3请参考CGI如何用C控制sqlite3?一文。
假设,QT程序已经与数据库建立了连接。此时,实例是静态的,断开连接后,静态对象并没有撤销,而是继续留在内存中,直到程序结束为止。www.2cto.com
QT在断开连接时,一直会占用该数据库。这样的后果是造成cgi程序无法与数据库建立连接。我们想的办法是,先将数据库与Qt断开连接。让cgi程序建立连接,使用完毕,断开连接。再建立Qt与数据库的连接。更加理想的方式是,每执行一次操作像cgi程序一样,执行类似建立连接->操作->断开连接 的过程。理想总是美好的,现实并非如此。
由于,Qt与数据的连接是单例的,只能创建一个实例。在断开连接后,实例并未被撤销。实际上,在退出程序前,都无法再与数据库建立连接了。连接被关闭的效果是,不能再利用该连接对数据库进行任何操作,并且无法再次连接,直到程序结束后重新开始。
*这段解释,给Qt 数据库编程敲响了一个警钟,在应用了单例模式的情况下,不要视图关闭数据库连接,“让道”给其他程序,然后重新进行连接。
实例2
在JavaMe 编程连载(9) - 重构之数据永久存储一文中,阐述了模拟一个通用数据库的方法。在文章最后,分析了单例模式的影响。如果程序中要建立多个实例,那么不要采用单例模式,编码实例是static的。
作者:tandesir