static 是C/C++中很經常使用的修飾符,它被用來控制變量的存儲方式和可見性。windows
咱們知道在函數內部定義的變量,當程序執行到它的定義處時,編譯器爲它在棧上分配空間,函數在棧上分配的空間在此函數執行結束時會釋放掉,這樣就產生了一個問題: 若是想將函數中此變量的值保存至下一次調用時,如何實現? 最容易想到的方法是定義爲全局的變量,但定義一個全局變量有許多缺點,最明顯的缺點是破壞了此變量的訪問範圍(使得在此函數中定義的變量,不僅受此函數控制)。static關鍵字則能夠很好的解決這個問題。數組
另外,在C++中,須要一個數據對象爲整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見時,可將其定義爲靜態數據。安全
全局(靜態)存儲區:分爲DATA段和BSS段。DATA段(全局初始化區)存放初始化的全局變量和靜態變量;BSS段(全局未初始化區)存放未初始化的全局變量和靜態變量。程序運行結束時自動釋放。其中BBS段在程序執行以前會被系統自動清0,因此未初始化的全局變量和靜態變量在程序執行以前已經爲0。存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是惟一的一次初始化。函數
在C++中static的內部實現機制:靜態數據成員要在程序一開始運行時就必須存在。由於函數在程序運行中被調用,因此靜態數據成員不能在任何函數內分配空間和初始化。
這樣,它的空間分配有三個可能的地方,一是做爲類的外部接口的頭文件,那裏有類聲明;二是類定義的內部實現,那裏有類的成員函數定義;三是應用程序的main()函數前的全局數據聲明和定義處。
靜態數據成員要實際地分配空間,故不能在類的聲明中定義(只能聲明數據成員)。類聲明只聲明一個類的「尺寸和規格」,並不進行實際的內存分配,因此在類聲明中寫成定義是錯誤的。它也不能在頭文件中類聲明的外部定義,由於那會形成在多個使用該類的源文件中,對其重複定義。
static被引入以告知編譯器,將變量存儲在程序的靜態存儲區而非棧上空間,靜態數據成員按定義出現的前後順序依次初始化,注意靜態成員嵌套時,要保證所嵌套的成員已經初始化了。消除時的順序是初始化的反順序。
優點:能夠節省內存,由於它是全部對象所公有的,所以,對多個對象來講,靜態數據成員只存儲一處,供全部對象共用。靜態數據成員的值對每一個對象都是同樣,但它的值是能夠更新的。只要對靜態數據成員的值更新一次,保證全部對象存取更新後的相同的值,這樣能夠提升時間效率。this
(1)生命週期:在修飾變量的時候,static修飾的靜態局部變量只執行初始化一次,並且延長了局部變量的生命週期,直到程序運行結束之後才釋放,但不改變做用域。好比修飾函數中存放在棧空間的數組。若是不想讓這個數組在函數調用結束釋放可使用static修飾。
(2)可見性:static修飾全局變量或函數時,這個全局變量只能在本文件中訪問,不能在其它文件中訪問,即使是extern外部聲明也不能夠。這個函數也只能在本文件中調用,不能被其餘文件調用。
(3)存儲方式:Static修飾的變量存放在全局數據區的靜態變量區,包括全局靜態變量和局部靜態變量,都在全局數據區分配內存。初始化的時候自動初始化爲0。
(4)考慮到數據安全性(當程序想要使用全局變量的時候應該先考慮使用static)。spa
(4)全局變量和全局靜態變量的區別.net
1 //example: 2 #include <stdio.h> 3 #include <stdlib.h> 4 int k1 = 1; 5 int k2; 6 static int k3 = 2; 7 static int k4; 8 int main() 9 { 10 static int m1 = 2, m2; 11 int i = 1; 12 char*p; 13 char str[10] = "hello"; 14 char*q = "hello"; 15 p = (char *)malloc(100); 16 free(p); 17 printf("棧區-變量地址 i:%p\n", &i); 18 printf("棧區-變量地址 p:%p\n", &p); 19 printf("棧區-變量地址 str:%p\n", str); 20 printf("棧區-變量地址 q:%p\n", &q); 21 printf("堆區地址-動態申請:%p\n", p); 22 printf("全局外部有初值 k1:%p\n", &k1); 23 printf(" 外部無初值 k2:%p\n", &k2); 24 printf("靜態外部有初值 k3:%p\n", &k3); 25 printf(" 外靜無初值 k4:%p\n", &k4); 26 printf(" 內靜態有初值 m1:%p\n", &m1); 27 printf(" 內靜態無初值 m2:%p\n", &m2); 28 printf(" 文字常量地址:%p, %s\n", q, q); 29 printf(" 程序區地址:%p\n", &main); 30 return 0; 31 }
static關鍵字最基本的用法是:線程
一、被static修飾的變量屬於類變量,能夠經過類名.變量名直接引用,而不須要new出一個類來指針
二、被static修飾的方法屬於類方法,能夠經過類名.方法名直接引用,而不須要new出一個類來code
被static修飾的變量、被static修飾的方法統一屬於類的靜態資源,是類實例之間共享的,換言之,一處變、到處變。
在C++中,靜態成員是屬於整個類的而不是某個對象,靜態成員變量只存儲一份供全部對象共用。因此在全部對象中均可以共享它。使用靜態成員變量實現多個對象之間的數據共享不會破壞隱藏的原則,保證了安全性還能夠節省內存。
靜態成員的定義或聲明要加個關鍵static。靜態成員能夠經過雙冒號來使用即<類名>::<靜態成員名>。
1 example1:經過類名調用靜態成員函數和非靜態成員函數 2 class Point 3 { 4 public: 5 void init() 6 { 7 } 8 static void output() 9 { 10 } 11 }; 12 void main() 13 { 14 Point::init(); 15 Point::output(); 16 }
報錯: 'Point::init' : illegal call of non-static member function
結論1:不能經過類名來調用類的非靜態成員函數。
1 //example2:經過類的對象調用靜態成員函數和非靜態成員函數 2 class Point 3 { 4 public: 5 void init() 6 { 7 } 8 static void output() 9 { 10 } 11 }; 12 void main() 13 { 14 Point pt; 15 pt.init(); 16 pt.output(); 17 }
編譯經過。
結論2:類的對象可使用靜態成員函數和非靜態成員函數。
1 //example3:在類的靜態成員函數中使用類的非靜態成員 2 #include <stdio.h> 3 class Point 4 { 5 public: 6 void init() 7 { 8 } 9 static void output() 10 { 11 printf("%d\n", m_x); 12 } 13 private: 14 int m_x; 15 }; 16 void main() 17 { 18 Point pt; 19 pt.output(); 20 }
編譯出錯:error C2597: illegal reference to data member 'Point::m_x' in a static member function
由於靜態成員函數屬於整個類,在類實例化對象以前就已經分配空間了,而類的非靜態成員必須在類實例化對象後纔有內存空間,因此這個調用就出錯了,就比如沒有聲明一個變量卻提早使用它同樣。
結論3:靜態成員函數中不能引用非靜態成員。
1 //example4:在類的非靜態成員函數中使用類的靜態成員 2 class Point 3 { 4 public: 5 void init() 6 { 7 output(); 8 } 9 static void output() 10 { 11 } 12 }; 13 void main() 14 { 15 Point pt;
15 Pt.init(); 16 pt.output(); 17 }
編譯經過。
結論4:類的非靜態成員函數能夠調用用靜態成員函數,但反之不能。
1 //example5:使用類的靜態成員變量 2 #include <stdio.h> 3 class Point 4 { 5 public: 6 Point() 7 { 8 m_nPointCount++; 9 } 10 ~Point() 11 { 12 m_nPointCount--; 13 } 14 static void output() 15 { 16 printf("%d\n", m_nPointCount); 17 } 18 private: 19 static int m_nPointCount; 20 }; 21 void main() 22 { 23 Point pt; 24 pt.output(); 25 }
按Ctrl+F7編譯無錯誤,按F7生成EXE程序時報連接錯誤
error LNK2001: unresolved external symbol "private: static int Point::m_nPointCount" (?m_nPointCount@Point@@0HA)
這是由於類的靜態成員變量在使用前必須先初始化。
在main()函數前加上int Point::m_nPointCount = 0;
再編譯連接無錯誤,運行程序將輸出1。
結論5:類的靜態成員變量必須先初始化再使用。
思考總結:靜態資源屬於類,可是是獨立於類存在的。從J類的加載機制的角度講,靜態資源是類初始化的時候加載的,而非靜態資源是類實例化對象的時候加載的。 類的初始化早於類實例化對象,好比Class.forName(「xxx」)方法,就是初始化了一個類,可是並無實例化對象,只是加載這個類的靜態資源罷 了。因此對於靜態資源來講,它是不可能知道一個類中有哪些非靜態資源的;可是對於非靜態資源來講就不同了,因爲它是實例化對象出來以後產生的,所以屬於類的這些東西它都能認識。因此上面的幾個問題答案就很明確了:
1)靜態方法能不能引用非靜態資源?不能,實例化對象的時候纔會產生的東西,對於初始化後就存在的靜態資源來講,根本不認識它。
2)靜態方法裏面能不能引用靜態資源?能夠,由於都是類初始化的時候加載的,你們相互都認識。
3)非靜態方法裏面能不能引用靜態資源?能夠,非靜態方法就是實例方法,那是實例化對象以後才產生的,那麼屬於類的內容它都認識。
(static修飾類:這個用得相對比前面的用法少多了,static通常狀況下來講是不能夠修飾類的, 若是static要修飾一個類,說明這個類是一個靜態內部類(注意static只能修飾一個內部類),也就是匿名內部類。像線程池 ThreadPoolExecutor中的四種拒絕機制CallerRunsPolicy、AbortPolicy、DiscardPolicy、 DiscardOldestPolicy就是靜態內部類。靜態內部類相關內容會在寫內部類的時候專門講到。)
(1)靜態成員函數中不能調用非靜態成員。
(2)非靜態成員函數中能夠調用靜態成員。由於靜態成員屬於類自己,在類的對象產生以前就已經存在了,因此在非靜態成員函數中是能夠調用靜態成員的。
(3)靜態成員變量使用前必須先初始化(如int MyClass::m_nNumber = 0;),不然會在linker時出錯。
參考:http://blog.csdn.net/morewindows/article/details/6721430
通常總結:在類中,static能夠用來修飾靜態數據成員和靜態成員方法
靜態數據成員
(1)靜態數據成員能夠實現多個對象之間的數據共享,它是類的全部對象的共享成員,它在內存中只佔一份空間,若是改變它的值,則各對象中這個數據成員的值都被改變。
(2)靜態數據成員是在程序開始運行時被分配空間,到程序結束以後才釋放,只要類中指定了靜態數據成員,即便不定義對象,也會爲靜態數據成員分配空間。
(3)靜態數據成員能夠被初始化,可是隻能在類體外進行初始化,若未對靜態數據成員賦初值,則編譯器會自動爲其初始化爲0
(4)靜態數據成員既能夠經過對象名引用,也能夠經過類名引用。
靜態成員函數 (1)靜態成員函數和靜態數據成員同樣,他們都屬於類的靜態成員,而不是對象成員。 (2)非靜態成員函數有this指針,而靜態成員函數沒有this指針。 (3)靜態成員函數主要用來方位靜態數據成員而不能訪問非靜態成員。