深入解析C++编程中的静态成员函数
C++靜態成員函數
與數據成員類似,成員函數也可以定義為靜態的,在類中聲明函數的前面加static就成了靜態成員函數。如
| 1 | staticint volume( ); |
和靜態數據成員一樣,靜態成員函數是類的一部分,而不是對象的一部分。
如果要在類外調用公用的靜態成員函數,要用類名和域運算符“::”。如
| 1 | Box::volume( ); |
實際上也允許通過對象名調用靜態成員函數,如
| 1 | a.volume( ); |
但這并不意味著此函數是屬于對象a的,而只是用a的類型而已。
與靜態數據成員不同,靜態成員函數的作用不是為了對象之間的溝通,而是為了能處理靜態數據成員。
我們知道,當調用一個對象的成員函數(非靜態成員函數)時,系統會把該對象的起始地址賦給成員函數的this指針。而靜態成員函數并不屬于某一對象,它與任何對象都無關,因此靜態成員函數沒有this指針。既然它沒有指向某一對象,就無法對一個對象中的非靜態成員進行默認訪問(即在引用數據成員時不指定對象名)。
可以說,靜態成員函數與非靜態成員函數的根本區別是:非靜態成員函數有this指針,而靜態成員函數沒有this指針。由此決定了靜態成員函數不能訪問本類中的非靜態成員。
靜態成員函數可以直接引用本類中的靜態數據成員,因為靜態成員同樣是屬于類的,可以直接引用。在C++程序中,靜態成員函數主要用來訪問靜態數據成員,而不訪問非靜態成員。
假如在一個靜態成員函數中有以下語句:
| 1 2 | cout<<height<<endl;//若height已聲明為static,則引用本類中的靜態成員,合法 cout<<width<<endl;//若width是非靜態數據成員,不合法 |
但是,并不是絕對不能引用本類中的非靜態成員,只是不能進行默認訪問,因為無法知道應該去找哪個對象。
如果一定要引用本類的非靜態成員,應該加對象名和成員運算符“.”。如
| 1 | cout<<a.width<<endl;//引用本類對象a中的非靜態成員 |
假設a已定義為Box類對象,且在當前作用域內有效,則此語句合法。
通過下面這個例子可以具體了解有關引用非靜態成員的具體方法。
[例] 靜態成員函數的應用。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <iostream> usingnamespace std; classStudent????????? //定義Student類 { public: ??Student(intn,inta,floats):num(n),age(a),score(s){ }?? //定義構造函數 ??voidtotal( ); ??staticfloat average( );?? //聲明靜態成員函數 private: ??intnum; ??intage; ??floatscore; ??staticfloat sum;????? //靜態數據成員 ??staticint count;????? //靜態數據成員 }; voidStudent::total( )?????????? //定義非靜態成員函數 { ??sum+=score;?????????????//累加總分 ??count++;???????????????//累計已統計的人數 } floatStudent::average( )???????? //定義靜態成員函數 { ??return(sum/count); } floatStudent::sum=0;?????????? //對靜態數據成員初始化 intStudent::count=0;?????????? //對靜態數據成員初始化 intmain( ) { ??Student stud[3]={?????????? //定義對象數組并初始化 ???Student(1001,18,70), ???Student(1002,19,78), ???Student(1005,20,98) ??}; ??intn; ??cout<<"please input the number of students:"; ??cin>>n;???????????????//輸入需要求前面多少名學生的平均成績 ??for(inti=0;i<n;i++)???????? //調用3次total函數 ???stud[i].total( ); ??cout<<"the average score of "<<n<<" students is "<<Student::average( )<<endl; ??//調用靜態成員函數 ??return0; } |
運行結果為:
| 1 2 | please input the number of students:3↙ the average score of 3 students is 82.3333 |
關于靜態成員函數成員的幾點說明:
在主函數中定義了stud對象數組,為了使程序簡練,只定義它含3個元素,分別存放3個學生的數據。程序的作用是先求用戶指定的n名學生的總分,然后求平均成績(n由用戶輸入)。
在Student類中定義了兩個靜態數據成員sum(總分)和count(累計需要統計的學生人數), 這是由于這兩個數據成員的值是需要進行累加的,它們并不是只屬于某一個對象元素,而是由各對象元素共享的,可以看出: 它們的值是在不斷變化的,而且無論對哪個對象元素而言,都是相同的,而且始終不釋放內存空間。
total是公有的成員函數,其作用是將一個學生的成績累加到sum中。公有的成員函數可以引用本對象中的一般數據成員(非靜態數據成員),也可以引用類中的靜態數據成員。score是非靜態數據成員,sum和count是靜態數據成員。
average是靜態成員函數,它可以直接引用私有的靜態數據成員(不必加類名或對象名), 函數返回成績的平均值。
在main函數中,引用total函數要加對象名(今用對象數組元素名), 引用靜態成員函數average函數要用類名或對象名。
請思考,如果不將average函數定義為靜態成員函數行不行?程序能否通過編譯?需要作什么修改?為什么要用靜態成員函數?請分析其理由。
C++ static靜態成員變量和靜態成員函數
一般情況下,如果有N個同類的對象,那么每一個對象都分別有自己的成員變量,不同對象的成員變量各自有值,互不相干。但是有時我們希望有某一個或幾個成員變量為所有對象共有,這樣可以實現數據共享。
可以使用全局變量來達到共享數據的目的。例如在一個程序文件中有多個函數,每一個函數都可以改變全局變量的值,全局變量的值為各函數共享。但是用全局變量的安全性得不到保證,由于在各處都可以自由地修改全局變量的值,很有可能偶然失誤,全局變量的值就被修改,導致程序的失敗。因此在實際開發中很少使用全局變量。
如果想在同類的多個對象之間實現數據共享,也不要用全局變量,那么可以使用靜態成員變量。
static靜態成員變量
靜態成員變量是一種特殊的成員變量,它以關鍵字 static 開頭。例如:
| 1 2 3 4 5 6 7 8 9 10 | classStudent{ private: ??char*name; ??intage; ??floatscore; ??staticint num; //將num定義為靜態成員變量 public: ??Student(char*, int,float); ??voidsay(); }; |
這段代碼聲明了一個靜態成員變量 num,用來統計學生的人數。
static 成員變量屬于類,不屬于某個具體的對象,這就意味著,即使創建多個對象,也只為 num 分配一份內存,所有對象使用的都是這份內存中的數據。當某個對象修改了 num,也會影響到其他對象。
static 成員變量必須先初始化才能使用,否則鏈接錯誤。例如:
| 1 | intStudent::num; //初始化 |
也可以在初始化時賦初值:
| 1 | intStudent::num = 10; //初始化同時賦值 |
初始化時可以不加 static,但必須要有數據類型。被 private、protected、public 修飾的 static 成員變量都可以用這種方式初始化。
注意:static 成員變量的內存空間既不是在聲明類時分配,也不是在創建對象時分配,而是在初始化時分配。
static 成員變量既可以通過對象來訪問,也可以通過類來訪問。通過類來訪問的形式為:
| 1 | 類名::成員變量; |
例如:
| 1 2 3 4 5 | //通過類來訪問 Student::num = 10; //通過對象來訪問 Student stu; stu.num = 10; |
這兩種方式是等效的。
注意:static 成員變量與對象無關,不占用對象的內存,而是在所有對象之外開辟內存,即使不創建對象也可以訪問。
下面來看一個完整的例子:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include <iostream> usingnamespace std; classStudent{ private: ??char*name; ??intage; ??floatscore; ??staticint num; //將num定義為靜態成員變量 public: ??Student(char*, int,float); ??voidsay(); }; intStudent::num = 0; //初始化靜態成員變量 Student::Student(char*name, intage, floatscore){ ??this->name = name; ??this->age = age; ??this->score = score; ??num++; } voidStudent::say(){ ??//在普通成員函數中可以訪問靜態成員變量 ??cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<"(當前共"<<num<<"名學生)"<<endl; } intmain(){ ??//使用匿名對象 ??(newStudent("小明", 15, 90))->say(); ??(newStudent("李磊", 16, 80))->say(); ??(newStudent("張華", 16, 99))->say(); ??(newStudent("王康", 14, 60))->say(); ??return0; } |
運行結果:
| 1 2 3 4 | 小明的年齡是 15,成績是 90(當前共1名學生) 李磊的年齡是 16,成績是 80(當前共2名學生) 張華的年齡是 16,成績是 99(當前共3名學生) 王康的年齡是 14,成績是 60(當前共4名學生) |
本例中將 num 聲明為靜態成員變量,每次創建對象時,會調用構造函數,將 num 的值加 1。之所以使用匿名對象,是因為每次創建對象后只會使用它的 say 函數,不再進行其他操作。不過請注意,使用匿名對象有內存泄露的風險。
關于靜態數據成員的幾點說明:?
1) 一個類中可以有一個或多個靜態成員變量,所有的對象都共享這些靜態成員變量,都可以引用它。
2) static 成員變量和普通 static 變量一樣,編譯時在靜態數據區分配內存,到程序結束時才釋放。這就意味著,static 成員變量不隨對象的創建而分配內存,也不隨對象的銷毀而釋放內存。而普通成員變量在對象創建時分配內存,在對象銷毀時釋放內存。
3) 靜態成員變量必須初始化,而且只能在類體外進行。例如:
| 1 | intStudent::num = 10; |
初始化時可以賦初值,也可以不賦值。如果不賦值,那么會被默認初始化,一般是 0。靜態數據區的變量都有默認的初始值,而動態數據區(堆區、棧區)的變量默認是垃圾值。
4) 靜態成員變量既可以通過對象名訪問,也可以通過類名訪問,但要遵循 private、protected 和 public 關鍵字的訪問權限限制。當通過對象名訪問時,對于不同的對象,訪問的是同一份內存。
static靜態成員函數
在類中,static 除了聲明靜態成員變量,還可以聲明靜態成員函數。普通成員函數可以訪問所有成員變量,而靜態成員函數只能訪問靜態成員變量。
我們知道,當調用一個對象的成員函數(非靜態成員函數)時,系統會把當前對象的起始地址賦給 this 指針。而靜態成員函數并不屬于某一對象,它與任何對象都無關,因此靜態成員函數沒有 this 指針。既然它沒有指向某一對象,就無法對該對象中的非靜態成員進行訪問。
可以說,靜態成員函數與非靜態成員函數的根本區別是:非靜態成員函數有 this 指針,而靜態成員函數沒有 this 指針。由此決定了靜態成員函數不能訪問本類中的非靜態成員。
靜態成員函數可以直接引用本類中的靜態數據成員,因為靜態成員同樣是屬于類的,可以直接引用。在C++程序中,靜態成員函數主要用來訪問靜態數據成員,而不訪問非靜態成員。
如果要在類外調用 public 屬性的靜態成員函數,要用類名和域解析符“::”。如:
| 1 | Student::getNum(); |
當然也可以通過對象名調用靜態成員函數,如:
| 1 | stu.getNum(); |
下面是一個完整的例子,通過靜態成員函數獲得學生的平均成績:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <iostream> usingnamespace std; classStudent{ private: ??char*name; ??intage; ??floatscore; ??staticint num; //學生人數 ??staticfloat total; //總分 public: ??Student(char*, int,float); ??voidsay(); ??staticfloat getAverage(); //靜態成員函數,用來獲得平均成績 }; intStudent::num = 0; floatStudent::total = 0; Student::Student(char*name, intage, floatscore){ ??this->name = name; ??this->age = age; ??this->score = score; ??num++; ??total += score; } voidStudent::say(){ ??cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<"(當前共"<<num<<"名學生)"<<endl; } floatStudent::getAverage(){ ??returntotal / num; } intmain(){ ??(newStudent("小明", 15, 90))->say(); ??(newStudent("李磊", 16, 80))->say(); ??(newStudent("張華", 16, 99))->say(); ??(newStudent("王康", 14, 60))->say(); ??cout<<"平均成績為 "<<Student::getAverage()<<endl; ??? ??return0; } |
運行結果:
| 1 2 3 4 5 | 小明的年齡是 15,成績是 90(當前共1名學生) 李磊的年齡是 16,成績是 80(當前共2名學生) 張華的年齡是 16,成績是 99(當前共3名學生) 王康的年齡是 14,成績是 60(當前共4名學生) 平均成績為 82.25 |
上面的代碼中,將 num、total 聲明為靜態成員變量,將 getAverage 聲明為靜態成員函數。在 getAverage 函數中,只使用了 total、num 兩個靜態成員變量。
總結
以上是生活随笔為你收集整理的深入解析C++编程中的静态成员函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++中的private protect
- 下一篇: C++中的内联函数inline总结