靜態資料成員和靜態成員函式

jsjliuyun發表於2014-06-08

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。

相關文章