來自:http://c.biancheng.net/cpp/biancheng/view/209.html
本文問題:類裡面對靜态成員變量的“定義”隻是聲明而已,必須在類外進行初始化(定義+指派),否則提示變量沒有定義,無法連結。
一般情況下,如果有N個同類的對象,那麼每一個對象都分别有自己的成員變量,不同對象的成員變量各自有值,互不相幹。但是有時我們希望有某一個或幾個成員變量為所有對象共有,這樣可以實作資料共享。
可以使用全局變量來達到共享資料的目的。例如在一個程式檔案中有多個函數,每一個函數都可以改變全局變量的值,全局變量的值為各函數共享。但是用全局變量的安全性得不到保證,由于在各處都可以自由地修改全局變量的值,很有可能偶然失誤,全局變量的值就被修改,導緻程式的失敗。是以在實際開發中很少使用全局變量。
如果想在同類的多個對象之間實作資料共享,也不要用全局變量,那麼可以使用靜态成員變量。
static靜态成員變量
靜态成員變量是一種特殊的成員變量,它以關鍵字static 開頭。例如:
1. class Student{
2. private:
3. char *name; int age; float score;
6. static int num; //将num定義為靜态成員變量,其實這裡隻是聲明而已,如果不在類外初始化,
// 會提示:undefined reference to 'Student::num'
8. public:
9. Student(char*,int,float); voidsay();
11.};
這段代碼聲明了一個靜态成員變量 num,用來統計學生的人數。
static 成員變量屬于類,不屬于某個具體的對象,這就意味着,即使建立多個對象,也隻為 num 配置設定一份記憶體,所有對象使用的都是這份記憶體中的資料。當某個對象修改了 num,也會影響到其他對象。
static 成員變量必須在類外先初始化才能使用,否則連結錯誤。例如:
int Student::num; //初始化
也可以在初始化時賦初值:
int Student::num = 10; //初始化同時指派
初始化時可以不加 static,但必須要有資料類型。被 private、protected、public修飾的 static 成員變量都可以用這種方式初始化。
注意:static 成員變量的記憶體空間既不是在聲明類時配置設定,也不是在建立對象時配置設定,而是在初始化時配置設定。
static 成員變量既可以通過對象來通路,也可以通過類來通路。通過類來通路的形式為:
類名::成員變量;
例如:
1. //通過類來通路
2. Student::num=10;
3. //通過對象來通路
4. Student stu;
5. stu.num=10;
這兩種方式是等效的。
注意:static 成員變量與對象無關,不占用對象的記憶體,而是在所有對象之外開辟記憶體,即使不建立對象也可以通路。
下面來看一個完整的例子:
1. #include<iostream>
2. using namespace std;
4. class Student{
5. private:
6. char *name; int age; float score;
9. static int num; //将num定義為靜态成員變量
11.public:
12. Student(char*,int,float); voidsay();
14.};
16.int Student::num=0; //初始化靜态成員變量
18.Student::Student(char*name,int age,float score){
19. this->name= name; this->age= age; this->score= score; num++;
23.}
24.void Student::say(){
25. //在普通成員函數中可以通路靜态成員變量
26. cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<"(目前共"<<num<<"名學生)"<<endl;
27.}
28.
29.int main(){
30. //使用匿名對象
31. (newStudent("小明",15,90))->say(); (newStudent("李磊",16,80))->say(); (newStudent("張華",16,99))->say(); (newStudent("王康",14,60))->say();
36. return0;
37.}
運作結果:
小明的年齡是 15,成績是 90(目前共1名學生)
李磊的年齡是 16,成績是 80(目前共2名學生)
張華的年齡是 16,成績是 99(目前共3名學生)
王康的年齡是 14,成績是 60(目前共4名學生)
本例中将 num 聲明為靜态成員變量,每次建立對象時,會調用構造函數,将 num 的值加 1。之是以使用匿名對象,是因為每次建立對象後隻會使用它的 say 函數,不再進行其他操作。不過請注意,使用匿名對象有記憶體洩露的風險。
關于靜态資料成員的幾點說明:
1) 一個類中可以有一個或多個靜态成員變量,所有的對象都共享這些靜态成員變量,都可以引用它。
2) static 成員變量和普通 static 變量一樣,編譯時在靜态資料區配置設定記憶體,到程式結束時才釋放。這就意味着,static 成員變量不随對象的建立而配置設定記憶體,也不随對象的銷毀而釋放記憶體。而普通成員變量在對象建立時配置設定記憶體,在對象銷毀時釋放記憶體。
3) 靜态成員變量必須初始化,而且隻能在類體外進行。例如:
int Student::num = 10;
初始化時可以賦初值,也可以不指派。如果不指派,那麼會被預設初始化,一般是 0。靜态資料區的變量都有預設的初始值,而動态資料區(堆區、棧區)的變量預設是垃圾值。
4) 靜态成員變量既可以通過對象名通路,也可以通過類名通路,但要遵循 private、protected 和 public 關鍵字的通路權限限制。當通過對象名通路時,對于不同的對象,通路的是同一份記憶體。
static靜态成員函數
1)在類中,static 除了聲明靜态成員變量,還可以聲明靜态成員函數。普通成員函數可以通路所有成員變量,而靜态成員函數隻能通路靜态成員變量。
2)當調用一個對象的成員函數(非靜态成員函數)時,系統會把目前對象的起始位址賦給 this 指針。而靜态成員函數并不屬于某一對象,它與任何對象都無關,是以靜态成員函數沒有 this 指針。既然它沒有指向某一對象,就無法對該對象中的非靜态成員進行通路。(靜态成員函數與非靜态成員函數的根本差別是:非靜态成員函數有 this 指針,而靜态成員函數沒有 this 指針。由此決定了靜态成員函數不能通路本類中的非靜态成員。)
3)靜态成員函數可以直接引用本類中的靜态資料成員,因為靜态成員同樣是屬于類的,可以直接引用。在C++程式中,靜态成員函數主要用來通路靜态資料成員,而不通路非靜态成員(除非對靜态成員函數傳入對象,通過對象來通路非靜态成員變量)。
4)如果要在類外調用 public 屬性的靜态成員函數,要用類名和域解析符“::”。如:
Student::getNum();
當然也可以通過對象名調用靜态成員函數,如:
stu.getNum();
5)出現在類體外的函數定義不能指定關鍵字static;出現在類體外的靜态成員變量初始化不可以指定關鍵字static
6)非靜态成員函數可以任意地通路靜态成員函數和靜态資料成員;類體外的其他任意函數可以通過傳入類對象任意修改公共靜态(非靜态)資料成員
7)靜态成員變量因為隻有一個記憶體位址,被所有對象所共有,是以無論怎麼修改其值,隻有最後一次修改有效
下面是一個完整的例子,通過靜态成員函數獲得學生的平均成績:
1. #include<iostream>
2. using namespace std;
4. class Student{
5. private:
6. char *name; int age; float score;
9. static int num; //學生人數
10. static float total; //總分
12.public:
13. Student(char*,int,float); void say();
15. static float getAverage(); //靜态成員函數,用來獲得平均成績
16.};
18.int Student::num=0;
19.float Student::total=0;
21.Student::Student(char*name,int age,float score){
22. this->name= name; this->age= age; this->score= score; num++; total+= score;
27.}
28.void Student::say(){
29. cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<"(目前共"<<num<<"名學生)"<<endl;
30.}
31.float Student::getAverage(){
32. return total/ num;
33.}
35.int main(){
36. (newStudent("小明",15,90))->say();
37. (newStudent("李磊",16,80))->say();
38. (newStudent("張華",16,99))->say();
39. (newStudent("王康",14,60))->say();
41. cout<<"平均成績為 "<<Student::getAverage()<<endl;
43. return 0;
44.}
運作結果:
小明的年齡是 15,成績是 90(目前共1名學生)
李磊的年齡是 16,成績是 80(目前共2名學生)
張華的年齡是 16,成績是 99(目前共3名學生)
王康的年齡是 14,成績是 60(目前共4名學生)
平均成績為 82.25
上面的代碼中,将 num、total 聲明為靜态成員變量,将 getAverage 聲明為靜态成員函數。在 getAverage 函數中,隻使用了 total、num 兩個靜态成員變量。