c++中的static靜態數據成員和靜態成員函數應該是讓大家比較頭疼的東西,好像也是找工作公司面試中常常問到的東西。我自己也深有體會,在學習c++的過程中,總感覺static很煩人,但是又是一個必須懂的東西,所以今天就對靜態數據成員和靜態成員函數坐下小結哈!
一、靜態數據成員
1.靜態數據成員怎麼去定義?
在類中聲明靜態數據成員很簡單,是以static關鍵字表明即可,如下所示
class Test{
private:
//靜態數據成員
static int a;
};
a就是靜態數據成員了,在類中只能聲明可是不能定義哈!
要對靜態數據成員定義和初始化必須在類的外面也就是在全局作用域中定義,如果定義不給出初值,則默認初值爲0
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.靜態數據成員被類的所有對象共享,包括該類的派生類對象,基類對象和派生類對象共享基類的靜態數據成員
答:靜態數據成員不屬於任何對象,類的靜態數據成員的存在不依賴於任何類對象的存在,靜態數據成員是由類的所有對象共享的。例子如下:
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.靜態數據成員可以作爲成員函數的默認形參,而普通數據成員則不可以
答:不多說,直接看例子馬上就明白了哈!
class Test{
public:
//靜態數據成員
static int a;
int b;
void fun_1(int i = a);//正確
void fun_2(int i = b);//報錯
};
4.靜態數據成員的類型可以是所屬類的類型,而普通數據成員則不可以。普通數據成員的只能聲明爲 所屬類類型的 指針或引用
答:這個也不多說,直接看例子就可以懂什麼意思哈!
class Test{
public:
//靜態數據成員
static Test a;//正確
Test b;//報錯
Test *pTest;//正確
Test &m_Test;//正確
static Test *pStaticObject;//正確
};
5.靜態數據成員在const函數中可以修改,而普通的數據成員是萬萬不能修改的哈!
答:看個例子
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打比賽,那就只能用那個靜態的籃球了(每個人都可以拿來用,但是帶來的影響是對全體的)。因此,我可以說那個籃球是高一一班的成員。所以也就是說:靜態成員函數是類的成員函數(因爲高一二班就不能拿來玩),但是這個籃球最後還是要還給老師的,任何私人不得佔有。希望這樣你能明白,其實在內存空間裏面說白了靜態的成員的內存是唯一的一份,就是當你在類外聲明他時開闢的,但是非靜態函數的空間分配是在你實例化對象時創建的。
爲了使大家更好的理解這兩個概念,還是老樣子用代碼來說明上面文字說明的這一切哈!
關於學生類的例子
//定義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。