(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