靜態(tài)成員由static關(guān)鍵字修飾, 分為靜態(tài)成員變量和靜態(tài)成員函數(shù), 特點(diǎn)是歸屬于類所有, 作用域是整個(gè)類, 而不是某一個(gè)對(duì)象, 換句話說靜態(tài)成員存儲(chǔ)于全局?jǐn)?shù)據(jù)區(qū)的, 不會(huì)因?qū)嵗龑?duì)象不同而有所改變。 下面對(duì)其幾個(gè)注意事項(xiàng)進(jìn)行描述:
一, 靜態(tài)成員在類內(nèi)聲明, 類外定義, 聲明時(shí)需要加上static關(guān)鍵字, 定義時(shí)不加static關(guān)鍵字, 但要加上作用域即類(類名::).
class A{
private:
static int b; //靜態(tài)成員變量
static void by_use(string s); //靜態(tài)成員函數(shù)
public:
A(){}
};
int A::b = 20; //類外定義靜態(tài)成員變量
void A::by_use(string s){ //定義靜態(tài)成員函數(shù)
std::cout << "by_use:" << s << b<< std::endl;
}
int main(){
...
return 0;
}
需要說明的幾個(gè)要點(diǎn):
const static類型的靜態(tài)成員變量可以在類內(nèi)進(jìn)行定義。 個(gè)人覺得原因如下:靜態(tài)變量是被所有類對(duì)象共享的, 當(dāng)然每一個(gè)類對(duì)象都可以進(jìn)行訪問, 如果靜態(tài)成員變量在類內(nèi)定義的話, 每一個(gè)對(duì)象初始化時(shí)都有可能對(duì)該靜態(tài)變量重新賦值, 從而造成其他對(duì)象的訪問錯(cuò)誤。如果定義為const static類型的話, 那么該變量的值不可變, 那么就不存在變量值變化的風(fēng)險(xiǎn), 所以可以類內(nèi)定義。
靜態(tài)成員函數(shù)也可以在類內(nèi)定義, 我的理解是這樣的, 函數(shù)不存在多次初始化賦值的現(xiàn)象, 因而也就不存在因某個(gè)對(duì)象初始化, 而不小心改變了值, 進(jìn)而影響其他對(duì)象的使用的情況的發(fā)生。
-
我們上面直接說了在類內(nèi)聲明靜態(tài)變量的時(shí)候需要使用關(guān)鍵字static進(jìn)行修飾,在類外定義static時(shí) 不要加上關(guān)鍵字, 只需要加上作用域就好。下面闡述原因:1, 首先介紹一下static的作用, 可以總結(jié)如下: static修飾的變量(函數(shù))會(huì)存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū)域, 使其生命周期為整個(gè)程序的運(yùn)行期間, 作用域?yàn)槎x該靜態(tài)變量的函數(shù)、文件或者類, (這里需要理解生命周期和作用域是不一樣的), 舉個(gè)簡(jiǎn)單例子:
void func(int b){ static int a = 10; a = b; ... } int main(){ func(20); func(30); ... return 0; }
上面的例子中我們?cè)趂unc()函數(shù)中定義了一個(gè)靜態(tài)變量a, 該變量在執(zhí)行main()函數(shù)之前就已經(jīng)被初始化, 且只被初始化一次, 它的生命周期是整個(gè)程序的運(yùn)行期間, 但是作用域僅限于定義它的func()函數(shù)中, func()函數(shù)運(yùn)行完之后, 會(huì)回收資源, 所有的普通成員變量會(huì)從棧中彈出, 但是靜態(tài)變量存儲(chǔ)于全局?jǐn)?shù)據(jù)區(qū), 所以它沒有銷毀, 程序運(yùn)行期間會(huì)一直存在(這是他的生存周期), 但是隨著func()函數(shù)運(yùn)行結(jié)束, 它也會(huì)變得不可見, 或者更準(zhǔn)確來說不能被其他函數(shù)“看見”(這由它的作用域決定), 下次我們?cè)僖淮握{(diào)用func()時(shí), 靜態(tài)變量a會(huì)被再次使用。就像上面的例子, 程序的最后, a的值應(yīng)該是30。
2,如果你看明白了上面的例子, 你應(yīng)該明白被static修飾的變量, 有這樣的特性:生存周期為整個(gè)程序的運(yùn)行時(shí)間, 作用域?yàn)槎x該變量的塊內(nèi)(函數(shù)或者類)。 好了, 我們回到正題, 在類內(nèi)使用static聲明靜態(tài)變量, 是使變量可以一直存在, 而且作用于該類。在類外定義該變量的時(shí)候不準(zhǔn)使用static, 是可以保證該變量的作用僅為定義它的類, 而不是當(dāng)前文件。想象一下, 如果在類外使用static定義了某靜態(tài)變量, 是不是就將該變量的作用域設(shè)置為當(dāng)前文件了(其他文件不可見), 那就不是類成員奧。
二、this指針與靜態(tài)成員
1, this指針的作用
我們?cè)陬惖某蓡T函數(shù)中可以去訪問該類的其他成員時(shí), 其中非靜態(tài)函數(shù)可以訪問靜態(tài)和非靜態(tài)成員, 靜態(tài)函數(shù)只能訪問靜態(tài)成員, 非靜態(tài)函數(shù)通過this指針去訪問非靜態(tài)成員,通過作用域訪問靜態(tài)成員。 當(dāng)然你沒有使用this指針去訪問非靜態(tài)成員也不會(huì)出錯(cuò), 因?yàn)樵诰幾g階段編譯器幫你加上了this指針。舉個(gè)例子:
class A{
private:
//定義成員變量
int a;
static int b;
//定義成員函數(shù)
void to_use(){ //用于講解的例子
a = 20;
b = 150;
use("hello word");
}
void use(string s){
std::cout << s << std::endl ;
}
public:
//構(gòu)造函數(shù)
A(){
a = 0;
}
A(int a){
this->a = a;
std::cout << a << std::endl;
}
};
int A::b = 50;
注意看to_use(string s)這個(gè)函數(shù), 這里面調(diào)用了類A的成員變量a、b,和成員函數(shù)use,其中變量b是靜態(tài)的。好了, 按照我們上面所述, 編譯器在編譯時(shí)會(huì)自動(dòng)在代碼中加上this->
去訪問非靜態(tài)成員, 而對(duì)于靜態(tài)成員不需要做什么特別的操作, 因?yàn)殪o態(tài)成員的作用域?yàn)樵擃悾?只需要直接調(diào)用就行。所以to_use(string s)函數(shù)中的代碼會(huì)被編譯器改為:
void to_use(){ //用于講解的例子
this->a = 20;
A::b = 150;
this->use("hello word");
}
2, this指針作為參數(shù)
由(1)中所知, 非靜態(tài)成員函數(shù)要想訪問其他非靜態(tài)成員必須使用this指針, 所以非靜態(tài)成員函數(shù)在被調(diào)用時(shí)隱含傳入一個(gè)this指針。與之對(duì)應(yīng)的,靜態(tài)成員函數(shù)在被調(diào)用時(shí)不會(huì)被傳入this指針, 其參數(shù)就是函數(shù)聲明的那幾個(gè)。
那么問題來了, 如果我們?cè)谀撤庆o態(tài)成員函數(shù)中直接調(diào)用另一個(gè)成員函數(shù)時(shí), 會(huì)將this指針作為隱含的參數(shù)傳遞過去, 這沒有問題(就像(1)中to_use()調(diào)用use(string s)那樣, 會(huì)將this傳過去), 但是如果被調(diào)用的函數(shù)是作為回調(diào)函數(shù)存在, this指針就無法作為參數(shù)傳遞給這個(gè)回調(diào)函數(shù)了, 所以回調(diào)函數(shù)必須定義為靜態(tài)成員函數(shù)。具體原因以及回調(diào)函數(shù)概念請(qǐng)見下節(jié)
3, 回調(diào)函數(shù)
將某函數(shù)A作為參數(shù)作為參數(shù)傳遞給另一給另一個(gè)函數(shù)B, 這個(gè)函數(shù)A就是回調(diào)函數(shù)。如下
qsort(arr0, len0, sizeof(int), &int_cmp);
qsort(arr0, len0, sizeof(int), int_cmp);
int_cmp是定義好的一個(gè)函數(shù), 在上面的兩個(gè)例子中, int_cmp就是回調(diào)函數(shù), 需要注意的是c++中函數(shù)作為參數(shù)傳遞時(shí), 傳入?yún)?shù)名或者對(duì)函數(shù)進(jìn)行取地址操作在傳參效果是一樣的, 因?yàn)樵摵瘮?shù)在被調(diào)用的時(shí)候也是對(duì)函數(shù)進(jìn)行取地址操作。
回調(diào)函數(shù)一定聲明為靜態(tài)成員
在類成員函數(shù)中, c++是默認(rèn)使用this對(duì)其他成員進(jìn)行訪問的, 當(dāng)在某一成員函數(shù)中調(diào)用其他普通成員函數(shù)時(shí), this也會(huì)作為默認(rèn)參數(shù)進(jìn)行傳遞(編譯器會(huì)做事情), 但是在該函數(shù)體內(nèi)出現(xiàn)的回調(diào)函數(shù)則沒有這個(gè)待遇, 也就是說在調(diào)用回調(diào)函數(shù)的時(shí)候, 不會(huì)將this作為參數(shù)傳遞給回調(diào)函數(shù), 那么如果回調(diào)函數(shù)被聲明為普通成員函數(shù)的話, (即沒有聲明為靜態(tài)類成員函數(shù)), 那么參數(shù)的個(gè)數(shù)是不匹配的, 因?yàn)樽鳛槠胀ǔ蓡T函數(shù)在調(diào)用時(shí)期待被傳入一個(gè)this指針的參數(shù), 但回調(diào)函數(shù)被調(diào)用時(shí)卻沒有接收到該參數(shù), 所以一定會(huì)出錯(cuò), 故回調(diào)函數(shù)一定聲明為靜態(tài)成員函數(shù)。例子:
class A{
private:
//定義成員變量
int a;
static int b;
//定義成員函數(shù)
void to_use(){ //用于講解的例子
a = 20;
b = 150;
use("hello word");
}
void use(void (*ptr)(string)){ //參數(shù)為回調(diào)函數(shù)
ptr("nihao");
}
static void by_use(string s){ //靜態(tài)成員函數(shù)
std::cout << "by_use:" << s << b<< std::endl;
}
public:
//構(gòu)造函數(shù)
A(){
a = 0;
}
A(int a){
this->a = a;
std::cout << a << std::endl;
}
};
int A::b = 50;
上例中, use(void (ptr)(string))函數(shù)的參數(shù)為函數(shù), 是典型的的回調(diào)函數(shù), 在to_use()中調(diào)用了use(void (ptr)(string)), this指針會(huì)作為參數(shù)傳遞給use函數(shù), 所以u(píng)se函數(shù)可以為非靜態(tài), 但是by_use函數(shù)作為回調(diào)函數(shù)在use()函數(shù)中使用, this指針是不會(huì)進(jìn)行傳遞的, 所以這里如果by_use函數(shù)不聲明為靜態(tài), 就會(huì)出錯(cuò), 因?yàn)閰?shù)不匹配。