C++的static有兩種用法:面向過程程序設計中的static和麪向對象程序設計中的static。前者應用於普通變量和函數,不涉及類;後者主要說明static在類中的做用。
1、面向過程設計中的static
一、靜態全局變量
在全局變量前,加上關鍵字static,該變量就被定義成爲一個靜態全局變量。咱們先舉一個靜態全局變量的例子,以下:ios
#include<iostream> using namespace std; static int n; //定義靜態全局變量 void fn() { n++; cout<<n<<endl; } int main(void) { n = 20; cout<<n<<endl; fn(); return 0; }
靜態全局變量有如下特色:函數
靜態變量都在全局數據區分配內存,包括後面將要提到的靜態局部變量。對於一個完整的程序,在內存中的分佈狀況以下圖:
this
代碼區 |
全局數據區 |
堆區 |
棧區 |
通常程序的由new產生的動態數據存放在堆區,函數內部的自動變量存放在棧區。自動變量通常會隨着函數的退出而釋放空間,靜態數據(即便是函數內部的靜態局部變量)也存放在全局數據區。全局數據區的數據並不會由於函數的退出而釋放空間。細心的讀者可能會發現,Example 1中的代碼中將spa
static int n; //定義靜態全局變量
改成設計
int n; //定義全局變量
程序照樣正常運行。
的確,定義全局變量就能夠實現變量在文件中的共享,但定義靜態全局變量還有如下好處:
靜態全局變量不能被其它文件所用;
其它文件中能夠定義相同名字的變量,不會發生衝突;
您能夠將上述示例代碼改成以下:指針
//File1 #include<iostream> using namespace std; void fn(); static int n; //定義靜態全局變量 int main(void) { n = 20; cout<<n<<endl; fn(); return 0; } //File2 #include<iostream> using namespace std; extern int n; void fn() { n++; cout<<n<<endl; }
編譯並運行這個程序,您就會發現上述代碼能夠分別經過編譯,但運行時出現錯誤。試着將code
static int n; //定義靜態全局變量
改成對象
int n; //定義全局變量
再次編譯運行程序,細心體會全局變量和靜態全局變量的區別。
二、靜態局部變量
在局部變量前,加上關鍵字static,該變量就被定義成爲一個靜態局部變量。
咱們先舉一個靜態局部變量的例子,以下:blog
#include<iostream> using namespace std; void fn(); int main(void) { fn(); fn(); fn(); return 0; } void fn() { static int n = 10; cout<<n<<endl; n++; }
一般,在函數體內定義了一個變量,每當程序運行到該語句時都會給該局部變量分配棧內存。但隨着程序退出函數體,系統就會收回棧內存,局部變量也相應失效。
但有時候咱們須要在兩次調用之間對變量的值進行保存。一般的想法是定義一個全局變量來實現。但這樣一來,變量已經再也不屬於函數自己了,再也不僅受函數的控制,給程序的維護帶來不便。
靜態局部變量正好能夠解決這個問題。靜態局部變量保存在全局數據區,而不是保存在棧中,每次的值保持到下一次調用,直到下次賦新值。
靜態局部變量有如下特色:
(1)該變量在全局數據區分配內存;
(2)靜態局部變量在程序執行到該對象的聲明處時被首次初始化,即之後的函數調用再也不進行初始化;
(3)靜態局部變量通常在聲明處初始化,若是沒有顯式初始化,會被程序自動初始化爲0;
(4)它始終駐留在全局數據區,直到程序運行結束。但其做用域爲局部做用域,當定義它的函數或語句塊結束時,其做用域隨之結束;
三、靜態函數
在函數的返回類型前加上static關鍵字,函數即被定義爲靜態函數。靜態函數與普通函數不一樣,它只能在聲明它的文件當中可見,不能被其它文件使用。
靜態函數的例子:內存
#include<iostream> using namespace std; static void fn(); //聲明靜態函數 int main(void) { fn(); return 0; } void fn() //定義靜態函數 { int n = 10; cout<<n<<endl; }
定義靜態函數的好處:
靜態函數不能被其它文件所用;
其它文件中能夠定義相同名字的函數,不會發生衝突;
2、面向對象的static關鍵字(類中的static關鍵字)
一、靜態數據成員
在類內數據成員的聲明前加上關鍵字static,該數據成員就是類內的靜態數據成員。先舉一個靜態數據成員的例子。
#include<iostream> using namespace std; class Myclass { private: int a , b , c; static int sum; //聲明靜態數據成員 public: Myclass(int a , int b , int c); void GetSum(); }; int Myclass::sum = 0; //定義並初始化靜態數據成員 Myclass::Myclass(int a , int b , int c) { this->a = a; this->b = b; this->c = c; sum += a+b+c; } void Myclass::GetSum() { cout<<"sum="<<sum<<endl; } int main(void) { Myclass M(1 , 2 , 3); M.GetSum(); Myclass N(4 , 5 , 6); N.GetSum(); M.GetSum(); return 0; }
能夠看出,靜態數據成員有如下特色:
二、靜態成員函數
與靜態數據成員同樣,咱們也能夠建立一個靜態成員函數,它爲類的所有服務而不是爲某一個類的具體對象服務。靜態成員函數與靜態數據成員同樣,都是類的內部實現,屬於類定義的一部分。普通的成員函數通常都隱含了一個this指針,this指針指向類的對象自己,由於普通成員函數老是具體的屬於某個類的具體對象的。一般狀況下,this是缺省的。如函數fn()其實是this->fn()。可是與普通函數相比,靜態成員函數因爲不是與任何的對象相聯繫,所以它不具備this指針。從這個意義上講,它沒法訪問屬於類對象的非靜態數據成員,也沒法訪問非靜態成員函數,它只能調用其他的靜態成員函數。下面舉個靜態成員函數的例子。
#include<iostream> using namespace std; class Myclass { private: int a , b , c; static int sum; //聲明靜態數據成員 public: Myclass(int a , int b , int c); static void GetSum(); //聲明靜態成員函數 }; int Myclass::sum = 0; //定義並初始化靜態數據成員 Myclass::Myclass(int a , int b , int c) { this->a = a; this->b = b; this->c = c; sum += a+b+c; //非靜態成員函數能夠訪問靜態數據成員 } void Myclass::GetSum() //靜態成員函數的實現 { //cout<<a<<endl; //錯誤代碼,a是非靜態數據成員 cout<<"sum="<<sum<<endl; } int main(void) { Myclass M(1 , 2 , 3); M.GetSum(); Myclass N(4 , 5 , 6); N.GetSum(); Myclass::GetSum(); return 0; }
關於靜態成員函數,能夠總結爲如下幾點: