c++中的static靜态資料成員和靜态成員函數應該是讓大家比較頭疼的東西,好像也是找工作公司面試中常常問到的東西。我自己也深有體會,在學習c++的過程中,總感覺static很煩人,但是又是一個必須懂的東西,是以今天就對靜态資料成員和靜态成員函數坐下小結哈!
一、靜态資料成員
1.靜态資料成員怎麼去定義?
在類中聲明靜态資料成員很簡單,是以static關鍵字表明即可,如下所示
[cpp] view plain copy
- class Test{
- private:
- //靜态資料成員
- static int a;
- };
a就是靜态資料成員了,在類中隻能聲明可是不能定義哈!
要對靜态資料成員定義和初始化必須在類的外面也就是在全局作用域中定義,如果定義不給出初值,則預設初值為0
[cpp] view plain copy
- class Test{
- public:
- int GetA() const{return a;}
- private:
- //靜态資料成員
- static int a;
- };
- //int Test::a;如果這樣定義不賦予初值則初值為零
- int Test::a = 1;
- #include <iostream>
- int main()
- {
- Test T;
- std::cout << T.GetA() << std::endl;
- }
定義時一定要在全局作用域中定義,一定不要在類中定義!
靜态資料成員甚至在類沒有任何對象的時候都可以通路,靜态成員可以獨立通路,無需依賴任何對象的建立
另外,不要試圖在頭檔案中定義(初始化)靜态資料成員。在大多數的情況下,這樣做會引起重複定義這樣的錯誤。即使加上#ifndef #define #endif或者#pragma once也不行。
2.靜态資料成員被類的所有對象共享,包括該類的派生類對象,基類對象和派生類對象共享基類的靜态資料成員
答:靜态資料成員不屬于任何對象,類的靜态資料成員的存在不依賴于任何類對象的存在,靜态資料成員是由類的所有對象共享的。例子如下:
[cpp] view plain copy
- class Base{
- public:
- //靜态資料成員
- static int a;
- };
- class Derived : public Base{
- };
- //int Test::a;如果這樣定義不賦予初值則初值為零
- int Base::a;
- #include <iostream>
- int main()
- {
- Base B;
- Derived D;
- B.a++;
- std::cout << B.a << std::endl;
- D.a++;
- std::cout << D.a << std::endl;
- }
列印結果如下:
由列印結果看出來,派生類對象和基類對象都是共享基類的靜态資料成員,而基類的所有對象也是共享該靜态資料成員,且該靜态資料成員應該在全局作用域中定義,可以賦予初值也可以不賦予初值,如果不賦予初值,靜态資料成員有其預設值。
3.靜态資料成員可以作為成員函數的預設形參,而普通資料成員則不可以
答:不多說,直接看例子馬上就明白了哈!
[cpp] view plain copy
- class Test{
- public:
- //靜态資料成員
- static int a;
- int b;
- void fun_1(int i = a);//正确
- void fun_2(int i = b);//報錯
- };
4.靜态資料成員的類型可以是所屬類的類型,而普通資料成員則不可以。普通資料成員的隻能聲明為 所屬類類型的 指針或引用
答:這個也不多說,直接看例子就可以懂什麼意思哈!
[cpp] view plain copy
- class Test{
- public:
- //靜态資料成員
- static Test a;//正确
- Test b;//報錯
- Test *pTest;//正确
- Test &m_Test;//正确
- static Test *pStaticObject;//正确
- };
5.靜态資料成員在const函數中可以修改,而普通的資料成員是萬萬不能修改的哈!
答:看個例子
[cpp] view plain copy
- class Test{
- public:
- Test():b(0){}
- //靜态資料成員
- static int a;//正确
- int b;
- void test() const
- {
- a++;
- b++;//const指的不能修改目前調用該函數對象的資料成員
- }
- };
- int Test::a;
const修飾的時目前this指針所指向的對象是const,但是靜态資料成員不屬于任何類的對象,它被類的所有對象修改,是以this指針不修飾靜态的資料成員,是以可以更改。
二、靜态成員函數
靜态成員函數的聲明也很簡單,就是在類的成員函數前加上static關鍵字即可,和靜态成員一樣,靜态成員函數也是屬于類的,它并不屬于任何對象,當調用靜态成員函數時應該使用類名和域運算符“∷”,當然也可以使用對象調用操作,但是這樣的操作并不意味着靜态成員函數屬于這個對象,它隻是被這個對象共享而已,這樣也就決定了靜态成員函數中是不能通路本類中的非靜态資料成員的,它是不能通路非靜态資料成員的,在c++中靜态成員函數主要用來通路靜态資料成員而不通路非靜态資料成員
1.靜态成員函數不能調用非靜态成員函數,但是反過來是可以的
2.靜态成員函數沒有this指針,也就是說靜态成員函數不能使用修飾符(也就是函數後面的const關鍵字)
3.靜态成員函數的位址可用普通函數指針儲存,而普通成員函數位址需要用 類成員函數指針來儲存。
總結:其實聲明為靜态,不論是靜态資料成員還是靜态成員函數,它們都是不依賴于對象而存在的,類在定義後并不配置設定存儲空間,而是在定義類的對象的時候才配置設定存儲空間,相反靜态的資料成員和靜态的成員函數是已經在記憶體中開辟了記憶體空間了,是以靜态資料成員可以獨立的通路在任何類對象沒有建立起來都可以通路,并且靜态成員函數不可以調用非靜态成員函數,因為非靜态成員函數隻有在類對象建立以後才可以調用,相反則是可以的。我覺得當對某個判斷産生懷疑的時候應該去測試,隻有驗證了才知道是不是對的哈!
為了能更好闡釋靜态資料成員和靜态成員函數,然後在網上搜了部落格,感覺有些例子不錯(因找不到最初的出處,是以無法注明請原作者諒解),是以就拿來給大家參考一下哈!
關于玩籃球的例子很能明顯解釋靜态資料成員和靜态成員函數到底是怎麼回事
你們班裡面有10個人(10個比如高一一班的對象),體育老師分給你們一個籃球(靜态成員函數),你們每個人都帶了一個籃球(非靜态成員數),你們都很小氣,自己的球隻能自己拍,要是5對5打比賽,那就隻能用那個靜态的籃球了(每個人都可以拿來用,但是帶來的影響是對全體的)。是以,我可以說那個籃球是高一一班的成員。是以也就是說:靜态成員函數是類的成員函數(因為高一二班就不能拿來玩),但是這個籃球最後還是要還給老師的,任何私人不得占有。希望這樣你能明白,其實在記憶體空間裡面說白了靜态的成員的記憶體是唯一的一份,就是當你在類外聲明他時開辟的,但是非靜态函數的空間配置設定是在你執行個體化對象時建立的。
為了使大家更好的了解這兩個概念,還是老樣子用代碼來說明上面文字說明的這一切哈!
關于學生類的例子
[cpp] view plain copy
- //定義Student類
- #include <iostream>
- class Student
- {
- public:
- //定義構造函數
- Student(int n,int a,float s):num(n),age(a),score(s){ }
- void total();
- //聲明靜态成員函數
- static float average();
- private:
- int num;
- int age;
- float score;
- //靜态資料成員,累計學生的總分
- static float sum;
- //靜态資料成員,累計學生的人數
- static int count;
- };
- //在全局作用域對靜态資料成員初始化,如果不賦予初值,則使用其預設值零
- float Student::sum;
- int Student::count;
- //定義非靜态成員函數
- void Student::total()
- {
- //累加總分
- sum+=score;
- //累計已統計的人數
- count++;
- }
- //定義靜态成員函數
- float Student::average()
- {
- return(sum/count);
- }
- int main()
- {
- Student stud[3]={
- //定義對象數組并初始化
- Student(1001,18,70),
- Student(1002,19,78),
- Student(1005,20,98)
- };
- int n;
- std::cout<<"please input the number of students: ";
- //輸入需要求前面多少名學生的平均成績
- std::cin>>n;
- //調用3次total函數
- for(int i=0;i<n;i++)
- {
- stud[i].total();
- }
- //調用靜态成員函數
- std::cout<<"the average score of "<<n<<" students is "<<Student::average( )<<std::endl;
- return 0;
- }
列印輸出如下:
對上面的代碼做以下說明:
首先,在主函數中定義了對象數組,存放每個學生的編号、年齡和成績,其中sum和count是要累計所有學生的總成績和總的學生人數,我們定義成了靜态資料成員,在學生類的成員函數中,我們定義了普通的total成員函數,用來計算所有學生的總成績和總的學生人數,另外,定義了靜态成員函數average,學生類的設計大概如此
在全局作用域對類中靜态資料成員進行了定義,但未賦予初值,這意味着我們采用其預設值。
在類的普通成員函數toal中可以引用靜态資料成員sum和count,可見類的所有成員函數都可以引用類的靜态資料成員。
在類的靜态成員函數average中,隻能引用類的靜态資料成員,不能引用非靜态資料成員。
在主函數中我們調用類的非靜态成員函數時隻能通過類對象來調用,如stu[i].total,但是對于靜态成員函數可以直接通過類名+作用域符号來調用,如
Student::average。
原文:http://blog.csdn.net/computer_liuyun/article/details/29235111