天天看點

3.C++: static靜态成員變量和靜态成員函數的用法集錦

來自: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 兩個靜态成員變量。

繼續閱讀