C/C++中的static關鍵字使用

static主要有三個做用ios

(1)局部靜態變量c++

(2)外部靜態變量/函數安全

(3)靜態數據成員/成員函數多線程

前兩種C和C++都有,第三種僅C++特有。下面分別介紹。ide

1、局部靜態變量函數

在C/C++中,局部變量按存儲方式可分爲auto,static,register,register不多用到,下面主要說說auto和static的區別。this

一、存儲空間分配和生存週期不一樣。
spa

二、static局部靜態變量在所處模塊初次運行時初始化,且只操做一次。線程

三、對於局部靜態變量,若是不賦初值,編譯時會自動賦初值0或空字符,而auto局部變量的初始值是不肯定的。(對於C++的class對象例外,class的對象實例若是不初始化,則會自動調用默認構造函數)設計

  • static局部靜態變量具備「記憶性」和生存期的「全局性」

「記憶性」是在再次調入函數時,static局部靜態變量保持前一次退出時的值。

#include <iostream>
using namespace std;
void localVar()
{
static int a=0;       //運行時初始化一次,下次調用時,不進行初始化
cout<< "a="<<a<<endl;
a++;
}
void main(void)
{
localVar();     //輸出a=0;
localVar();     //記憶了第一次退出時的值,輸出a=1
}

「全局性」可以延長變量的生存期, 改善」return a pointer / reference to a local object」的問題。例如:

const char * IpToStr(UINT32 IpAddr)
{
 static char strBuff[16]; // static局部變量, 用於返回地址有效
 const unsigned char *pChIP = (const unsigned char *)&IpAddr;
 sprintf(strBuff, "%u.%u.%u.%u",  pChIP[0], pChIP[1], pChIP[2], pChIP[3]);
 return strBuff;
}


注意:因爲static具備全局惟一性的特色,每次調用時,都指向同一塊內存,這就形成了一個很嚴重的問題——不可重入性。這在多線程程序設計和遞歸程序設計中要特別注意。下面針對上述示例分析多線程狀況下的不安全性。

假設如今有兩個線程A,B運行期間都須要調用IpToStr()函數, 將32位的IP地址轉換成點分10進制的字符串形式. 現A先得到執行機會, 執行IpToStr(), 傳入的參數是0x0B090A0A, 順序執行完應該返回的指針存儲區內容是:」10.10.9.11」, 現執行到⑥時, 失去執行權, 調度到B線程執行, B線程傳入的參數是0xA8A8A8C0, 執行至⑦, 靜態存儲區的內容是192.168.168.168. 當再調度到A執行時, 從⑥繼續執行, 因爲strBuff的全局惟一性, 內容已經被B線程沖掉, 此時返回的將是192.168.168.168字符串, 再也不是10.10.9.11字符串。

2、外部靜態變量/函數

在C中static有第二種含義:用來表示不能被其餘文件訪問的全局變量和函數,即在變量和函數前加static,是爲了限制其做用域僅限於本文件(因此又稱內部函數)。不管是否有static限制, 它的存儲區域都是在靜態存儲區, 生存期都是全局的. 此時的static只是起做用域限制做用, 限定做用域在本模塊(文件)內部。使用內部函數的好處是:不一樣的人編寫不一樣的函數時,不用擔憂本身定義的函數,是否會與其它文件中的函數同名。例如:

#fiel1.cpp

static int a=0;

int b;

extern void funA()

{}

static void funB()

{}

#file2.cpp

extern int a;//錯誤

extern int b;//正確

extern void funA()//正確

extern void funB()//錯誤


3、靜態數據成員/成員函數(C++特有)

c++重用了這個關鍵字,並賦予其第三個含義:表示靜態數據成員/成員函數屬於一個類,而不屬於某個對象,這是與普通成員函數的最大區別。 好比在對某一個類的對象進行計數時, 計數生成多少個類的實例, 就能夠用到靜態數據成員. 在這裏面, static既不是限定做用域的, 也不是擴展生存期的做用, 而是指示變量/函數在此類中的惟一性. 這也是」屬於一個類而不是屬於此類的任何特定對象的變量和函數」的含義. 由於它是對整個類來講是惟一的, 所以不可能屬於某一個實例對象的。

先舉一個靜態數據成員的例子

  1. #include<iostream>  

  2. using namespace std;   

  3. class Myclass  

  4. {  

  5. private:  

  6.     int a , b , c;  

  7.     static int sum;  //聲明靜態數據成員  

  8. public:  

  9.     Myclass(int a , int b , int c);  

  10.     void GetSum();  

  11. };   

  12. int Myclass::sum = 0;   //定義並初始化靜態數據成員    

  13. Myclass::Myclass(int a , int b , int c)  

  14. {  

  15.     this->a = a;  

  16.     this->b = b;  

  17.     this->c = c;  

  18.     sum += a+b+c;  

  19. }  

  20. void Myclass::GetSum()  

  21. {  

  22.     cout<<"sum="<<sum<<endl;  

  23. }  

  

  1. int main(void)  

  2. {  

  3.     Myclass M(1 , 2 , 3);  

  4.     M.GetSum();  //輸出結果6

  5.     Myclass N(4 , 5 , 6);  

  6.     N.GetSum();  //輸出結果21

  7.     return 0;  

  8. }  

靜態成員函數

與靜態數據成員同樣,也是類的內部實現,屬於類的內部實現,

普通的成員函數通常隱含了this指針,指向類對象自己,由於普通成員函數老是具體的屬於某個類的具體對象的一般狀況下,this是缺省的。如函數fn()其實是this->fn()。可是與普通函數相比,靜態成員函數因爲不是與任何的對象相聯繫,所以它不具備this指針。從這個意義上講,它沒法訪問屬於類對象的非靜態數據成員,也沒法訪問非靜態成員函數,它只能調用其他的靜態成員函數。舉例:


  1. #include<iostream>  

  2. using namespace std;  

  3.   

  4. class Myclass  

  5. {  

  6. private:  

  7.     int a , b , c;  

  8.     static int sum;  //聲明靜態數據成員  

  9. public:  

  10.     Myclass(int a , int b , int c);  

  11.     static void GetSum();  //聲明靜態成員函數  

  12. };  

  13.   

  14. int Myclass::sum = 0;   //定義並初始化靜態數據成員  

  15.   

  16. Myclass::Myclass(int a , int b , int c)  

  17. {  

  18.     this->a = a;  

  19.     this->b = b;  

  20.     this->c = c;  

  21.     sum += a+b+c;    //非靜態成員函數能夠訪問靜態數據成員  

  22. }  

  23. void Myclass::GetSum()    //靜態成員函數的實現  

  24. {  

  25.     //cout<<a<<endl;    //錯誤代碼,a是非靜態數據成員  

  26.     cout<<"sum="<<sum<<endl;  

  27. }  

  28. int main(void)  

  29. {  

  30.     Myclass M(1 , 2 , 3);  

  31.     M.GetSum();  

  32.     Myclass N(4 , 5 , 6);  

  33.     N.GetSum();  

  34.     Myclass::GetSum();  

  35.     return 0;  

  36. }  


另外, 在設計類的多線程操做時, 因爲POSIX庫下的線程函數pthread_create()要求是全局的, 普通成員函數沒法直接作爲線程函數, 能夠考慮用Static成員函數作線程函數。

相關文章
相關標籤/搜索