前言:借鑒他人+個人了解
————————————————————————————————————————————————————————————————————————————
一個類中也可以包含靜态成員和非靜态成員,類中也包括靜态構造函數和非靜态構造函數..
分兩個方面來總結,第一方面主要是相對于面向過程而言,即在這方面不涉及到類,第二方面相對于面向對象而言,主要說明static在類中的作用。
一、在面向過程設計中的static關鍵字
1、靜态全局變量
定義:在全局變量前,加上關鍵字 static 該變量就被定義成為了一個靜态全局變量。
//file1.cpp
//Example 1
#include <iostream.h>
void fn();
<span style="color:#33CC00;"> static int n; //定義靜态全局變量</span>
void main()
{
n=20;
cout<<n<<endl; //輸出 20
fn(); //輸出 21
}
void fn()
{
n++;
cout<<n<<endl;
}
特點:
A、該變量在全局資料區配置設定記憶體。
B、初始化:如果不顯式初始化,那麼将被隐式初始化為0(自動變量是随機的,除非顯式地初始化)。
C、訪變量隻在本源檔案可見,嚴格的講應該為定義之處開始到本檔案結束。
D、檔案作用域下聲明的const的常量預設為static存儲類型。
靜态變量都在全局資料區配置設定記憶體,包括後面将要提到的靜态局部變量。對于一個完整的程式,在記憶體中的分布情況如下圖:
代碼區 |
全局資料區 |
堆區 |
棧區 |
一般程式的由new産生的動态資料存放在堆區,函數内部的自動變量存放在棧區。自動變量一般會随着函數的退出而釋放空間,靜态資料(即使是函數内部的靜态局部變量)也存放在全局資料區。全局資料區的資料并不會因為函數的退出而釋放空間。細心的讀者可能會發現,Example 1中的代碼中将
static int n; //定義靜态全局變量
改為:
int n; //定義全局變量
程式照樣正常運作。的确,定義全局變量就可以實作變量在檔案中的共享,但定義靜态全局變量還有以下好處:
- 靜态全局變量不能被其它檔案所用;(好像是差別extern的)
- 其它檔案中可以定義相同名字的變量,不會發生沖突;
可以将上述示例代碼改為如下:
//Example 2
//File1
#include <iostream.h>
void fn();
static int n; //定義靜态全局變量(隻能在本檔案中使用)
void main()
{
n=20;
cout<<n<<endl;
fn();
}
//File2
#include <iostream.h>
extern int n;(可在别的檔案中引用這個變量)
void fn()
{
n++;
cout<<n<<endl;
}
編譯并運作Example 2,會發現上述代碼可以分别通過編譯,但link時出現錯誤。試着将
static int n; //定義靜态全局變量
改為
int n; //定義全局變量
再次編譯運作程式,細心體會全局變量和靜态全局變量的差別。
2、靜态局部變量
定義:在局部變量前加上static關鍵字時,就定義了靜态局部變量。
我們先舉一個靜态局部變量的例子,如下:
//Example 3
#include <iostream.h>
void fn();
void main()
{
fn();
fn();
fn();
}
void fn()
{
static int n=10; //定義靜态局部變量
cout<<n<<endl;
n++;
}
運作結果:
通常,在函數體内定義了一個變量,每當程式運作到該語句時都會給該局部變量配置設定棧記憶體。但随着程式退出函數體,系統就會收回棧記憶體,局部變量也相應失效。
但有時候我們需要在兩次調用之間對變量的值進行儲存。通常的想法是定義一個全局變量來實作。但這樣一來,變量已經不再屬于函數本身了,不再僅受函數的控制,給程式的維護帶來不便。
靜态局部變量正好可以解決這個問題。靜态局部變量儲存在全局資料區,而不是儲存在棧中, 每次的值保持到下一次調用,直到下次賦新值。
特點:
A、該變量在全局資料區配置設定記憶體。
B、初始化:如果不顯式初始化,那麼将被隐式初始化為0,以後的函數調用不再進行初始化。
C、它始終駐留在全局資料區,直到程式運作結束。但其作用域為局部作用域,當定義它的函數或 語句塊結束時,其作用域随之結束。
3、靜态函數(注意與類的靜态成員函數差別)
定義:在函數的傳回類型前加上static關鍵字,函數即被定義成靜态函數。
特點:
A、靜态函數與普通函數不同,它隻能在聲明它的檔案當中可見,不能被其它檔案使用。
靜态函數的例子:
//Example 4
#include <iostream.h>
static void fn();//聲明靜态函數,有些編譯器允許在類體内定義靜态成員函數
void main()
{
fn();
}
void fn()//定義靜态函數
{
int n=10;
cout<<n<<endl;
}
定義靜态函數的好處:
- 靜态函數不能被其它檔案所用;
- 其它檔案中可以定義相同名字的函數,不會發生沖突;
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、面向對象的static關鍵字(類中的static關鍵字)
1、靜态資料成員
在類内資料成員的聲明前加上關鍵字static,該資料成員就是類内的靜态資料成員。先舉一個靜态資料成員的例子。
#include <iostream.h>
class Myclass { public: Myclass(int a,int b,int c); void GetSum(); private: int a,b,c; static int Sum;//聲明靜态資料成員,Sum不屬于任何一個對象私有,是以所有對象不存儲該資料 }; int Myclass::Sum=0;//類體外定義初始化: 不加 static,在靜态存儲區配置設定空間,隻能定義一次。當不設初值時,預設為0 Myclass::Myclass(int a,int b,int c) { this->a=a; this->b=b; this->c=c; Sum+=a+b+c; } void Myclass::GetSum() { cout<<"Sum="<<Sum<<endl; } void main() { Myclass M(1,2,3); M.GetSum(); Myclass N(4,5,6); N.GetSum(); M.GetSum(); }
運作結果:
可以看出,靜态資料成員有以下特點:
- 對于非靜态資料成員,每個類對象都有自己的拷貝。而靜态資料成員被當作是類的成員。無論這個類的對象被定義了多少個,靜态資料成員在程式中也隻有一份拷貝,由該類型的所有對象共享通路。也就是說,靜态資料成員是該類的所有對象所共有的。對該類的多個對象來說,靜态資料成員隻配置設定一次記憶體,供所有對象共用。是以,靜态資料成員的值對每個對象都是一樣的,它的值可以更新。也就是說靜态資料成員對于所有類對象來說隻有一份,是以不能在類體内随着每個類對象的初始化而初始化而是需要在類體外進行單獨的初始化。
- 靜态資料成員存儲在全局資料區。靜态資料成員定義時要配置設定空間,是以不能在類聲明(例如class Myclass())中定義,隻能在其中聲明靜态資料成員。因為類聲明隻聲明一個類的“尺寸和規格”,并不進行實際的記憶體配置設定,是以在類聲明中寫定義是錯誤的。它也不能在頭檔案中類聲明的外部定義,因為那會造成當多個使用該類的源檔案中,對其重複定義。在Example 5中,語句int Myclass::Sum=0;是定義初始化靜态資料成員;(注:有些編譯器也支援類中初始化,static const int Sum = 3;)
- 靜态資料成員和普通資料成員一樣遵從public,protected,private通路規則;
- 因為靜态資料成員在全局資料區配置設定記憶體,屬于本類的所有對象共享,是以,它不屬于特定的類對象,在沒有産生類對象時其作用域就可見,即在沒有産生類的執行個體時,我們就可以操作它;,<類類型名>::<允許通路的靜态資料成員名>
- 靜态資料成員初始化與一般資料成員初始化不同。初始化在類體外進行,而前面不加 static,以免與一般靜态變量或對象相混淆。static int Base::data = 10;這樣的話,static并不在類的作用域内(并不在Base::後面),是以就得把static做一般解釋了,那就是定義一個全局靜态變量,也就是在其他源檔案中是不能使用的。
靜态資料成員 初始化的格式 為:
<資料類型><類名>::<靜态資料成員名>=<值> //例如 上述例子中的 int Myclass::Sum=0;
-
類的靜态資料成員有兩種通路形式:
<類對象名>.<靜态資料成員名> 或 <類類型名>::<靜态資料成員名> //若Sum改為public成員,則可用 M.Sum 或者 Myclass::Sum進行通路
如果靜态資料成員的通路權限允許的話(即public的成員),可在程式中,按上述格式來引用靜态資料成員 ;
- 靜态資料成員主要用在各個對象都有相同的某項屬性的時候。比如對于一個存款類,每個執行個體的利息都是相同的。是以,應該把利息設為存款類 的靜态資料成員。這有兩個好處,第一,不管定義多少個存款類對象,利息資料成員都共享配置設定在全局資料區的記憶體,是以節省存儲空間。第二,一旦利息需要改變時,隻要改變一次,則所有存款類對象的利息全改變過來了,進而提高時間效率;
- 同全局變量相比,使用靜态資料成員有兩個優勢:
- 靜态資料成員沒有進入程式的全局名字空間,是以不存在與程式中其它全局名字沖突的可能性;(見下面例子)
- 可以實作資訊隐藏。靜态資料成員可以是private成員,而全局變量不能;
......... int Sum = 3;
void main() { Myclass M(1,2,3); M.GetSum(); //指的是 Myclass類對象M中的Sum。 輸出 Sum=6 cout<<Sum<<endl; //指的是全局變量Sum,同Myclass類對象M中的Sum是不起沖突的。輸出 3 }
2、靜态成員函數
與靜态資料成員一樣,我們也可以建立一個靜态成員函數,它為類的全部服務而不是為某一個類的具體對象服務。靜态成員函數與靜态資料成員一樣,都是類的内部實作,屬于類定義的一部分。普通的成員函數一般都隐含了一個this指針,this指針指向類的對象本身,因為普通成員函數總是具體的屬于某個 類的具體對象的。通常情況下,this是預設的。如函數fn()實際上是this->fn()。但是與普通函數相比,靜态成員函數由于不是與任何的對象相聯系,是以它不具有this指針。從這個意義上講,它無法通路屬于類對象的非靜态資料成員,也無法通路非靜态成員函數,它隻能調用其餘的靜态資料成員和靜态成員函數。下面舉個靜态成員函數的例子。
//Example 6
#include <iostream.h>
class Myclass
{
public:
Myclass(int a,int b,int c);
static void GetSum();/聲明靜态成員函數 有些編譯器允許在類體内定義靜态成員函數
private:
int a,b,c;
static int Sum;//聲明靜态資料成員
};
int Myclass::Sum=0;//定義并初始化靜态資料成員
Myclass::Myclass(int a,int b,int c)
{
this->a=a;
this->b=b;
this->c=c;
Sum+=a+b+c; //非靜态成員函數可以通路靜态資料成員
}
void Myclass::GetSum() ① //靜态成員函數的實作
{
// cout<<a<<endl; //錯誤,a是非靜态資料成員,靜态成員函數無法通路非靜态資料成員或非靜态函數
cout<<"Sum="<<Sum<<endl;
}
void main()
{
Myclass M(1,2,3);
M.GetSum();
Myclass N(4,5,6);
N.GetSum();
Myclass::GetSum();
}
關于靜态成員函數,可以總結為以下幾點:
- 出現在類體外的函數定義不能指定關鍵字static;//例如上述例子中的語句①處;
- 靜态成員函數不能通路非靜态成員函數和非靜态資料成員,而在靜态成員之間可以互相通路,包括靜态成員函數通路靜态資料成員和通路靜态成員函數;
- 不能将靜态成員函數定義為虛函數;
- 非靜态成員函數可以任意地通路靜态成員函數和靜态資料成員;
- 由于沒有this指針的額外開銷,是以靜态成員函數與類的全局函數相比速度上會有少許的增長;
- 調用靜态成員函數,可以用成員通路操作符(.)和(->)為一個類的對象或指向類對象的指針調用靜态成員函數;
擴充: public,protected,private通路規則:
1) public:共有成員,本類、子類以及類外都可以通路,是通路受限最少的成員; 2) protected:保護成員,隻運作在本類或子類中可以通路,類外則無法通路。注:友元函數也可以通路; 3) private:私有成員,隻在本類中可以通路。注:其友元函數也可以通路。
C++派生類成員的通路屬性:
在派生類中,對基類的繼承方式可以有public(公用的)、private (私有的)和protected(保護的)3種。不同的繼承方式決定了基類成員在派生類中的通路屬性。簡單地說可以總結為以下幾點。 1) 公用繼承(public inheritance) 基類的公用成員和保護成員在派生類中保持原有通路屬性,其私有成員仍為基類私有。 私有成員的意思是,不能被外界引用,隻能被本類中函數或者該類的友元函數通路。 2) 私有繼承(private inheritance) 基類的公用成員和保護成員在派生類中成了私有成員,其私有成員仍為基類私有。 3) 受保護的繼承(protected inheritance) 基類的公用成員和保護成員在派生類中成了保護成員,其私有成員仍為基類私有。 保護成員的意思是,不能被外界引用,但可以被派生類的成員引用。
int main()
{
...............
int m; // 局部變量存放在動态存儲區,運作時存在,若無初始化,則對應的值是一個不确定的值,這是由于每次函數
<span style="font-family:Microsoft YaHei;"> //</span>調用結束後存儲單元被釋放,下次調用時又重新另配置設定存儲單元,而所配置設定的單元中内容是不知的
cout<<m<<endl; //輸出内容不可知
static int k; //靜态變量存放在靜态存儲區,編譯時就存在的,必須進行初始化,或者編譯時預設自動指派為0
cout<<k<<endl; //輸出 0
............
return 0;
}
<特此鳴謝>
借鑒自:
http://blog.csdn.net/searchlife/article/details/2770140
http://www.cnblogs.com/daocaoren/archive/2011/06/29/2092957.html