靜態函數
用static聲明的函數是靜態函數。靜態函數可以分為全局靜態函數和類的靜態成員函數。
Static關鍵字
在類中,用static聲明的成員變量為靜態成員變量,它為該類的公用變量,在第一次使用時被初始化,對于該類的所有對象來說,static成員變量只有一份。
用static聲明的方法是靜態方法,在調用該方法時,不會將對象的引用傳遞給它,所以在static方法中不可訪問非static的成員。
靜態方法不再是針對于某個對象調用,所以不能訪問非靜態成員。
可以通過對象引用或類名(不需要實例化)訪問靜態成員
C++類靜態數據成員與類靜態成員函數
函數調用的結果不會訪問或者修改任何對象(非static)數據成員,這樣的成員聲明為靜態成員函數比較好。且如果static int func(....)不是出現在類中,則它不是一個靜態成員函數,只是一個普通的全局函數,只不過由于static的限制,它只能在文件所在的編譯單位內使用,不能在其它編譯單位內使用。
靜態成員函數的聲明除了在類體的函數聲明前加上關鍵字static,以及不能聲明為const或者volatile之外,與非靜態成員函數相同。出現在類體之外的函數定義不能制定關鍵字static。
靜態成員函數沒有this指針。
在沒有講述本章內容之前如果我們想要在一個范圍內共享某一個數據,那么我們會設立全局對象,但面向對象的程序是由對象構成的,我們如何才能在類范圍內共享數據呢?
這個問題便是本章的重點:聲明為static的類成員或者成員函數便能在類的范圍內共同享,我們把這樣的成員稱做靜態成員和靜態成員函數。
下面我們用幾個實例來說明這個問題,類的成員需要保護,通常情況下為了不違背類的封裝特性,我們是把類成員設置為protected(保護狀態)的,但是我們為了簡化代碼,使要說明的問題更為直觀,更容易理解,我們在此處都設置為public。
以下程序我們來做一個模擬訪問的例子,在程序中,每建立一個對象我們設置的類靜態成員變自動加一,代碼如下:
#include <iostream>
using namespace std;
class Internet
{
public:
Internet(char *name,char *address)
{
strcpy(Internet::name,name);
strcpy(Internet::address,address);
count++;
}
static void Internet::Sc()//靜態成員函數
{
cout<<count<<endl;
}
Internet &Rq();
public:
char name[20];
char address[20];
static int count;//這里如果寫成static int count=0;就是錯誤的
};
Internet& Internet::Rq()//返回引用的成員函數
{
return *this;
}
int Internet::count = 0;//靜態成員的初始化
void vist()
{
Internet a1("中國軟件開發實驗室","www.cndev-lab.com");
Internet a2("中國軟件開發實驗室","www.cndev-lab.com");
}
void fn(Internet &s)
{
cout<<s.Rq().count;
}
void main()
{
cout<<Internet::count<<endl;//靜態成員值的輸出
vist();
Internet::Sc();//靜態成員函數的調用
Internet b("中國軟件開發實驗室","www.cndev-lab.com");
Internet::Sc();
fn(b);
cin.get();
}
上面代碼我們用了幾種常用的方式建立對象,當建立新對象并調用其構造函數的時候,靜態成員cout便運行加1操作,靜態成員的初始化應該在主函數調用之前,并且不能在類的聲明中出現,通過運行過程的觀察我們發現,靜態成員count的狀態并不會隨著一個新的對象的新建而重新定義,盡而我們了解到類的靜態成員是屬于類的而不是屬于哪一個對象的,所以靜態成員的使用應該是類名稱加域區分符加成員名稱的,在上面的代碼中就是Internet::count,雖然我們仍然可以使用對象名加點操作符號加成員名稱的方式使用,但是不推薦的,靜態類成員的特性就是屬于類而不專屬于某一個對象。
靜態成員函數的特性類似于靜態成員的使用,同樣與對象無關,調用方法為類名稱加域區分符加成員函數名稱,在上面的代碼中就是Internet::Sc();靜態成員函數由于與對象無關系,所以在其中是不能對類的普通成員進行直接操作的。
如果上面的 static void Internet::Sc()修改成為:
static void Internet::Sc()//靜態成員函數
{
cout<<name<<endl;//錯誤
cout<<count<<endl;
}
靜態成員函數與普通成員函數的差別就在于缺少this指針,沒有這個this指針自然也就無從知道name是哪一個對象的成員了。
根據類靜態成員的特性我們可以簡單歸納出幾點,靜態成員的使用范圍:
1.用來保存對象的個數。
2.作為一個標記,標記一些動作是否發生,比如:文件的打開狀態,打印機的使用狀態,等等。
3.存儲鏈表的第一個或者最后一個成員的內存地址。
為了做一些必要的練習,深入的掌握靜態對象的存在的意義,我們以前面的結構體的教程為基礎,用類的方式描述一個線性鏈表,用于存儲若干學生的姓名,代碼如下:
#include <iostream>
using namespace std;
class Student
{
public:
Student (char *name);
~Student();
public:
char name[30];
Student *next;
static Student *point;
};
Student::Student(char *name)
{
strcpy(Student::name,name);
this->next=point;
point=this;
}
Student::~Student ()//析構過程就是節點的脫離過程
{
cout<<"析構:"<<name<<endl;
if(point==this)
{
point=this->next;
cin.get();
return;
}
for(Student *ps=point;ps;ps=ps->next)
{
if(ps->next==this)
{
cout<<ps->next<<""<<this->next<<endl;
ps->next=next;//=next也可以寫成this->next;
cin.get();
return;
}
}
cin.get();
}
Student* Student::point=NULL;
void main()
{
Student *c = new Student("marry");
Student a("colin");
Student b("jamesji");
delete c;
Student *fp=Student::point;
while(fp!=NULL)
{
cout<<fp->name<<endl;
fp=fp->next;
}
cin.get();
}
從上面的代碼來看,原來單純結構化編程需要的一個鏈表進入全局指針在這里被類的靜態成員指針所替代(類的靜態成員完全可以替代全局變量),這個例子的理解重點主要是要注意觀察類成員的析構順序,通過對析構順序的理解,使用析構函數來進行節點的脫鏈操作。
為什么虛函數必須是非靜態成員函數
如果定義為虛函數,那么它就是動態綁定的,也就是在派生類中可以被覆蓋的,這與靜態成員函數的定義本身就是相矛盾的。
==
主要有兩個作用:
1、管理靜態數據成員;
2、提供類范圍的功能,即不需要對象來實現的功能。
比如Symbian中的NewL/LC方法就是static的
==
使用static關鍵字聲明的函數成員是靜態的,靜態成員函數同樣也屬于整個類,由同一個類的所有對象共同維護,為這些對象所共享。
作為成員函數,它的訪問屬性可以受到類的嚴格控制,對于公有的靜態函數成員函數,可以通過類名或對象名來調用,但一般情況下建議用對象名來引用靜態函數成員(真的嗎?)。注意,一般的成員函數只能通過對象名來調用。
由于一個類的靜態成員函數只有一個拷貝,因此它訪問對象的數據何函數使受到了限制。靜態成員函數可以直接訪問該類的靜態數據成員。而訪問非靜態數據成員,必須通過參數傳遞方式得到對象名,然后通過對象名來訪問。可以看到,通過靜態函數成員訪問非靜態成員使相當麻煩的,一般的使用中,它主要用來訪問全局變量或同一個類中的靜態數據成員,特別是和后者一起使用,達到對同一個類中對象之間共享的數據進行維護的目的。
構造函數不可以定義為static,看了上面,應該可以理解原因。
注意,由于static不是函數類型中的一部分,所以在類定義之外定義靜態成員函數時不使用static,在類中定義的靜態成員函數是內聯的。
一般來說,通過成員名限定比使用對象名訪問靜態成員要好。因為靜態成員不是對象的成員。
靜態成員可以被繼承,這時,基類對象和派生類的對象共享該靜態成員,除此之外,在類等級中對靜態成員的其他特性(例如,靜態成員在派生類中的訪問權限,在派生類中重載成員函數等)的分析與一般成員類似。
靜態成員函數不能被申明為虛函數,靜態成員具有外部連接屬性,static僅有的含義是使該成員為該類的所有對象共享。
類中的任何成員函數都可以訪問靜態成員,但靜態成員函數只能通過對象名(或指向對象的指針)訪問該對象的非靜態成員,因為靜態成員函數沒有this 指針。
虛函數必須是非靜態成員函數
構造函數不可以定義為static