程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 設計模式(2)-單例模式(Singleton)

設計模式(2)-單例模式(Singleton)

編輯:C++入門知識

(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  

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
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved