static小結(jié)
static的引入
static 是C++中很常用的修飾符,它被用來控制變量的存儲方式和可見性。
函數(shù)內(nèi)部定義的變量,在程序執(zhí)行到它的定義處時,編譯器為它在棧上分配空間,函數(shù)在棧上分配的空間在此函數(shù)執(zhí)行結(jié)束時會釋放掉,這樣就產(chǎn)生了一個問題:如果想將函數(shù)中此變量的值保存至下一次調(diào)用時,如何實(shí)現(xiàn)? 最容易想到的方法是定義一個全局的變量,但定義為一個全局變量有許多缺點(diǎn),最明顯的缺點(diǎn)是破壞了此變量的訪問范圍(使得在此函數(shù)中定義的變量,不僅僅受此函數(shù)控制)。而static變量可以保持該變量的值,使得函數(shù)在下次調(diào)用時還保持上次函數(shù)退出時的值。
static內(nèi)部機(jī)制
static被引入以告知編譯器,將變量存儲在程序的靜態(tài)存儲區(qū)而非棧上空間,靜態(tài)數(shù)據(jù)成員按定義出現(xiàn)的先后順序依次初始化,注意靜態(tài)成員嵌套時,要保證所嵌套的成員已經(jīng)初始化了。
static的優(yōu)勢:
可以節(jié)省內(nèi)存,因?yàn)樗撬袑ο笏械?,因此,對多個對象來說,靜態(tài)數(shù)據(jù)成員只存儲一處,供所有對象共用。靜態(tài)數(shù)據(jù)成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時間效率。
static類成員
類的對象構(gòu)建過程不會動到static變量和函數(shù),因?yàn)樗嬖陟o態(tài)內(nèi)存,程序加載進(jìn)內(nèi)存的時候它就存在;而對象生命周期不同。
static數(shù)據(jù)成員
static數(shù)據(jù)成員要在程序一開始運(yùn)行時就必須存在。因?yàn)楹瘮?shù)在程序運(yùn)行中被調(diào)用,所以靜態(tài)數(shù)據(jù)成員不能在任何函數(shù)內(nèi)分配空間和初始化。
static數(shù)據(jù)成員要實(shí)際地分配空間,故不能在類的聲明中定義(只能聲明數(shù)據(jù)成員),也不能在頭文件中類聲明的外部定義,因?yàn)槟菚斐稍诙鄠€使用該類的源文件中,對其重復(fù)定義。
static數(shù)據(jù)成員必須在類定義體的外部定義(正好一次)。不像普通數(shù)據(jù)成員,static成員不是通過類構(gòu)造函數(shù)進(jìn)行初始化,而是應(yīng)該在定義時進(jìn)行初始化。
- 在類中,靜態(tài)成員可以實(shí)現(xiàn)多個對象之間的數(shù)據(jù)共享,并且使用靜態(tài)數(shù)據(jù)成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態(tài)成員是類的所有對象中共享的成員,而不是某個對象的成員。
- 靜態(tài)數(shù)據(jù)成員可以節(jié)省內(nèi)存,因?yàn)樗撬袑ο笏械?,因此,對多個對象來說,靜態(tài)數(shù)據(jù)成員只存儲一處,供所有對象共用。靜態(tài)數(shù)據(jù)成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時間效率。
- 初始化格式為 <數(shù)據(jù)類型><類名>::<靜態(tài)數(shù)據(jù)成員名>=<值> 。初始化在類體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或?qū)ο笙嗷煜?/li>
static成員函數(shù)
- 在靜態(tài)成員函數(shù)的實(shí)現(xiàn)中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時,可通過對象來引用。
- 靜態(tài)成員函數(shù),不屬于任何一個具體的對象,那么在類的具體對象聲明之前就已經(jīng)有了內(nèi)存區(qū),而非靜態(tài)數(shù)據(jù)成員還沒有分配內(nèi)存空間,那么在靜態(tài)成員函數(shù)中使用非靜態(tài)成員函數(shù),就好像沒有聲明一個變量卻提前使用它一樣。
- static成員函數(shù)沒有this指針,不能被聲明為const,不能被聲明為虛函數(shù)。
單例模式(Singleton)
介紹
單例(Singleton)模式是保證一個類僅有一個實(shí)例,并提供一個訪問它的全局訪問點(diǎn)。
有很多地方需要這樣的功能模塊,如系統(tǒng)的日志輸出,GUI應(yīng)用必須是單鼠標(biāo),MODEM的聯(lián)接需要一條且只需要一條電話線,操作系統(tǒng)只能有一個窗口管理器,一臺PC連一個鍵盤。
Singleton模式讓類自身負(fù)責(zé)保存它的唯一實(shí)例。這個類可以保證沒有其他實(shí)例可以被創(chuàng)建(通過截取創(chuàng)建新對象的請求),并且它可以提供一個訪問該實(shí)例的方法。
適用性
- 當(dāng)類只能有一個實(shí)例而且客戶可以從一個眾所周知的訪問點(diǎn)訪問它時。
- 當(dāng)這個唯一實(shí)例應(yīng)該是通過子類化可擴(kuò)展的,并且客戶應(yīng)該無需更改代碼就能使用一個
擴(kuò)展的實(shí)例時。
結(jié)構(gòu)

實(shí)現(xiàn)
保證一個唯一的實(shí)例
將創(chuàng)建唯一實(shí)例的操作隱藏在一個類操作(即一個靜態(tài)成員函數(shù))后面,由它保證只有一個實(shí)例被創(chuàng)建。這個操作可以訪問保存唯一實(shí)例的變量,而且它可以保證這個變量在返回值之前用這個唯一實(shí)例初始化。
在C++中,你可以用Singleton類的靜態(tài)成員函數(shù)Instance來定義這個類操作。Singleton還定義一個靜態(tài)成員變量_instance,它包含了一個指向它的唯一實(shí)例的指針。
Singleton類定義:
class Singleton{
public:
static Singleton* Instance();
protected:
Singleton();
//構(gòu)造函數(shù)為protected,當(dāng)試圖直接實(shí)例化Singleton類時,會得到編譯時的錯誤信息
//保證僅有一個實(shí)例可以被創(chuàng)建
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance() {
if(_instance == 0){
_instance = new Singleton;
}
return _instance;
}
釋放Singleton對象
單例類Singleton有以下特征:
它有一個指向唯一實(shí)例的靜態(tài)指針_pInstance,并且是私有的;
它有一個公有的函數(shù),可以獲取這個唯一的實(shí)例,并且在需要的時候創(chuàng)建該實(shí)例;
它的構(gòu)造函數(shù)是私有的,這樣就不能從別處創(chuàng)建該類的實(shí)例。
我們需要一種方法,正常的刪除該實(shí)例
一個妥善的方法是讓這個類自己知道在合適的時候把自己刪除,或者說把刪除自己的操作掛在操作系統(tǒng)中的某個合適的點(diǎn)上,使其在恰當(dāng)?shù)臅r候被自動執(zhí)行。
程序在結(jié)束的時候,系統(tǒng)會自動析構(gòu)所有的全局變量。事實(shí)上,系統(tǒng)也會析構(gòu)所有的類的靜態(tài)成員變量,就像這些靜態(tài)成員也是全局變量一樣。利用這個特征,我們可以在單例類中定義一個這樣的靜態(tài)成員變量,而它的唯一工作就是在析構(gòu)函數(shù)中刪除單例類的實(shí)例。
class CSingleton
{
private:
CSingleton()
{
}
static CSingleton *m_pInstance;
class CGarbo //它的唯一工作就是在析構(gòu)函數(shù)中刪除CSingleton的實(shí)例
{
public:
~CGarbo()
{
if(CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo; //定義一個靜態(tài)成員變量,程序結(jié)束時,系統(tǒng)會自動調(diào)用它的析構(gòu)函數(shù)
public:
static CSingleton * GetInstance()
{
if(m_pInstance == NULL) //判斷是否第一次調(diào)用
m_pInstance = new CSingleton();
return m_pInstance;
}
};
類CGarbo被定義為CSingleton的私有內(nèi)嵌類,以防該類被在其他地方濫用。
程序運(yùn)行結(jié)束時,系統(tǒng)會調(diào)用CSingleton的靜態(tài)成員Garbo的析構(gòu)函數(shù),該析構(gòu)函數(shù)會刪除單例的唯一實(shí)例。
使用這種方法釋放單例對象有以下特征:
在單例類內(nèi)部定義專有的嵌套類;
在單例類內(nèi)定義私有的專門用于釋放的靜態(tài)成員;
利用程序在結(jié)束時析構(gòu)全局變量的特性,選擇最終的釋放時機(jī);
使用單例的代碼不需要任何操作,不必關(guān)心對象的釋放。
創(chuàng)建Singleton類的子類(單件注冊表方法)
使用單件注冊表(registry of singleton),使得singleton類可以根據(jù)名字在一個眾所周知的注冊表中注冊它們的單件實(shí)例。
這個注冊表在字符串名字和單件之間建立映射。當(dāng)Instance需要一個單件時,它參考注冊表,根據(jù)名字請求單件。
class Singleton{
public:
static void Register(const char* name, Singleton *);
static Singleton* Instance();
protected:
static Singleton* Lookup(const char* name);
private:
static Singleton* _instance;
static List<NameSingletonPair>* _registry;
};
Register以給定的名字注冊Singleton實(shí)例。為保證注冊簡單,我們將它存儲一列NameSingletonPair對象。 每個NameSingletonPair將一個名字映射到一個單件。Lookup操作根據(jù)給定單件的名字進(jìn)行查找。
Singleton* Singleton::Instance(){
if(_instance == 0){
const char* singletonName = getenv("SINGLETON");
//environment supplies this at startup
_instance = Lookup(singletonName);
//Lookup returns 0 if there's no such singleton
}
return _instance;
}
Singleton類可以在構(gòu)造函數(shù)中來注冊自己。
MySingleton::MySingleton() {
Sinleton::Register("MySingleton",this);
}
由此,Singleton類不再負(fù)責(zé)創(chuàng)建單件。它的主要職責(zé)是使得供選擇的單件對象在系統(tǒng)中可以被訪問。