轉自:http://blog.csdn.net/wanglongfei_hust/article/details/10011503
static關鍵字有三種使用方式,其中前兩種只指在C語言中使用,第三種在C++中使用。
1. 局部靜態變量(C)
2. 外部靜態變量/函數(C)
3. 靜態數據成員/成員函數(C++)
一、 局部靜態變量
局部變量按照存儲形式可以分為三種,分別是auto、static、register。
與auto類型(普通)局部變量相比,static有三點不同:
1. 存儲空間分配不同
auto類型分配在棧上,屬於動態存儲類別,占動態存儲空間,函數調用結束后自動釋放;
static類型分配在靜態存儲區,在程序整個運行期間都不釋放;
兩者作用域相同,但是生存期不同。
2. static局部變量在初次運行時進行初始化工作,且只初始化一次。
3. 對於局部靜態變量,如果不賦初值,編譯期會自動賦初值0或者空;
auto類型的初值是不確定的。
對於C++的類對象例外,class的對象實例如果不初始化,則會自動調用默認構造函數,不管是不是static類型。
特點:static局部變量的“記憶性”與生存期的“全局性”
所謂“記憶性”是指在兩次函數調用時,在第二次調用進入時,能保持第一次調用退出時的值。
示例程序一
- #include <iostream>
- using namespace std;
- void staticLocalVar()
- {
- static int a = 0;
- cout<<"a="<<++a<<endl;
- }
- int main()
- {
- staticLocalVar(); // a=1
- staticLocalVar(); // a=2
- system("pause");
- return 0;
- }
運行結果:
a=1
a=2
請按任意鍵繼續. . .
應用:利用“記憶性”記錄函數調用的次數(示例程序一)
利用生存期的”全局性“改善return a pointer / reference to a local object的問題,local object的問題在於退出函數時,生存期就結束,局部變量就會被銷毀;利用static就可以延長局部變量的生存期。
注意事項:
1. “記憶性”是程序運行很重要的一點就是可重復性,而static變量的“記憶性”破壞了可重復性,造成不同時刻同一函數的運行結果不同。
2. “生存期”全局性和唯一性。 普通的局部變量在棧上分配空間,因此每次調用函數時,分配的空間都可能不一樣,而static具有全局唯一性的特點,每次調用時都指向同一塊內存,這就造成一個很重要的問題---不可重入性!!!
在多線程或者遞歸程序中要特別注意。
二、 外部靜態變量/函數
在C中static的第二種含義:用來表示不能被其它文件訪問的全局變量和函數。
此處static的含義是指對函數的作用域僅僅局限於本文件(所以又稱為內部函數)。
注意:對於外部(全局)變量,不論是否有static限制,它的存儲區域都是在靜態存儲區,生存期都是全局的,此時的static只是起作用域限制作用,限制作用域在本文件內部。
使用內部函數的好處是:不同的人編寫不同的函數時,不用擔心函數同名問題。
示例程序二
- //file1.cpp
- static int varA;
- int varB;
- extern void funA()
- {
- }
- static void funB()
- {
- }
- //file2.cpp
- extern int varB; // 使用file1.cpp中定義的全局變量
- extern int varA; // 錯誤! varA是static類型, 無法在其他文件中使用
- extern void funA(); // 使用file1.cpp中定義的函數
- extern void funB(); // 錯誤! 無法使用file1.cpp文件中static函數
三、 靜態數據成員/成員函數(C++特有)
C++重用了這個關鍵字,它表示屬於一個類而不是屬於此類的任何特定的對象的變量和函數。
靜態類成員包括靜態數據成員和靜態函數成員。
1. 靜態數據成員
類體中的數據成員的聲明前加上static關鍵字,該數據成員就成為了該類的靜態數據成員。和其他數據成員一樣,靜態數據成員也遵守public/protected/private訪問規則。同時靜態數據成員還具有以下特點。
1) 靜態數據成員的定義
靜態數據成員實際上是類域中的全局變量。所以,靜態數據成員的定義(初始化)不應該被放在頭文件中。其定義方式與全局變量相同。舉例如下:
- xxx.h文件
- class base
- {
- private:
- static const int _i; //聲明,標准c++支持有序類型在類體中初始化,但vc6不支持。
- };
- xxx.cpp文件
- const int base::_i = 10; //定義(初始化)時不受private和protected訪問限制.
注:不要試圖在頭文件中定義(初始化)靜態數據成員。在大多數情況下,這會引起重復定義。即使加上#ifndef #define #endif或者#pragma once也不行。
2) 靜態數據成員被類的所有對象所共享,包括該類的派生類的對象。
- #include <iostream>
- using namespace std;
- class base
- {
- public:
- static int _num; //聲明
- };
- int base::_num = 0; //靜態數據成員的真正定義
- class derived : public base
- {
- };
- int main()
- {
- base a;
- derived b;
- a._num++;
- cout<<"base class static data number _num is "<<a._num<<endl; // 1
- b._num++;
- cout<<"derived class static data number _num is "<<b._num<<endl;// 2
- system("pause");
- return 0;
- }
3) 靜態數據成員可以成為成員函數的可選參數,而普通數據成員則不可以。
- class base
- {
- public:
- static int _staticVar;
- int _var;
- void foo1(int i = _staticVar);//正確,_staticVar為靜態數據成員
- void foo2(int i = _var);//錯誤,_var為普通數據成員
- };
4)★靜態數據成員的類型可以是所屬類的類型,而普通數據成員則不可以。普通數據成員的只能聲明為所屬類類型的指針或引用。舉例如下:
- class base
- {
- public:
- static base _object1;//正確,靜態數據成員
- base object2;//錯誤
- base *pObject;//正確,指針
- base &mObject;//正確,引用
- };
5) 靜態數據成員的值在const成員函數中可以被合法的改變。舉例如下:
- class base
- {
- public:
- base()
- {
- _i = 0;
- _val = 0;
- }
- mutable int _i;
- static int _staticVal;
- int _val;
- void test() const
- {
- _i++;//正確,mutable數據成員
- _staticVal++;//正確,static數據成員
- _val++;//錯誤
- }
- };
- int base::_staticVal = 0;
2. 靜態成員函數
1).靜態成員函數的地址可用普通函數指針儲存,而普通成員函數地址需要用類成員函數指針來儲存。舉例如下:
- class base
- {
- static int func1();
- int func2();
- };
- int (*pf1)() = &base::func1; //普通的函數指針
- int (base::*pf2)() = &base::func2; //成員函數指針
2).靜態成員函數不可以調用類的非靜態成員。因為靜態成員函數不含this指針。
3).靜態成員函數不可以同時聲明為 virtual、const、volatile函數。舉例如下:
- class base
- {
- virtual static void func1();//錯誤
- static void func2() const;//錯誤
- static void func3() volatile;//錯誤
- };
最后要說的一點是,靜態成員是可以獨立訪問的,也就是說,無須創建任何對象實例就可以訪問。
