天天看點

C++靜态成員變量和靜态成員函數C++靜态成員變量和靜态成員函數

C++靜态成員變量和靜态成員函數

[轉]原文

注意:

類中靜态變量在類外初始化格式:

<資料類型><類名>::<靜态資料成員名>=<值>

加上聲明是為了在構造函數之前運作。

private: 
int a,b,c; 
static const int Sum[];//聲明靜态資料成員!!!!!!!!!!!!!!!  
}; 
const int Myclass::Sum={,,,,,};//定義并初始化靜态資料成員!!!!!!!! 
           

資料成員可以分靜态變量、非靜态變量兩種.

靜态成員: 靜态類中的成員加入static修飾符,即是靜态成員.可以直接使用類名::靜态成員名通路此靜态成員,因為靜态成員存在于記憶體,非靜态成員需要執行個體化才會配置設定記憶體,是以靜态成員不能通路非靜态的成員.因為靜态成員存在于記憶體,是以非靜态成員可以直接通路類中靜态的成員.

非成靜态員: 所有沒有加Static的成員都是非靜态成員,當類被執行個體化之後,可以通過執行個體化的類名進行通路.非靜态成員的生存期決定于該類的對象的生存期..而靜态成員則不存在生存期的概念,因為靜态成員始終駐留在内容中..

一個類中也可以包含靜态成員和非靜态成員,類中也包括靜态構造函數和非靜态構造函數..

分兩個方面來總結,第一方面主要是相對于面向過程而言,即在這方面不涉及到類,第二方面相對于面向對象而言,主要說明static在類中的作用。

一、在面向過程設計中的static關鍵字

1、靜态全局變量

定義:在全局變量前,加上關鍵字 static 該變量就被定義成為了一個靜态全局變量。

特點:

-   A、該變量在全局資料區配置設定記憶體。

-   B、初始化:如果不顯式初始化,那麼将被隐式初始化為0(自動變量是随機的,除非顯式地初始化)。

-   C、訪變量隻在本源檔案可見,嚴格的講應該為定義之處開始到本檔案結束。

-   D、檔案作用域下聲明的const的常量預設為static存儲類型。

  例(摘于C++程式設計教程—錢能主編P103): “`

  //file1.cpp 
  //Example 1 
    #include <iostream.h> 
    void fn(); 
    static int n; //定義靜态全局變量 
    void main() 
    { 
        n=; 
        cout < <n < <endl; 
        fn(); 
    } 

    void fn() 
    { 
        n++; 
        cout < <n < <endl; 
    } 
           

靜态變量都在全局資料區配置設定記憶體,包括後面将要提到的靜态局部變量。對于一個完整的程式,在記憶體中的分布情況如下:

代碼區

全局資料區

堆區

棧區

  一般程式的由new産生的動态資料存放在堆區,函數内部的自動變量存放在棧區。自動變量一般會随着函數的退出而釋放空間,靜态資料(即使是函數内部的靜态局部變量)也存放在全局資料區。全局資料區的資料并不會因為函數的退出而釋放空間。細心的讀者可能會發現,Example 1中的代碼中将

static int n; //定義靜态全局變量 
           

改為:

int n; //定義全局變量程式照樣正常運作。的确,定義全局變量就可以實作變量在檔案中的共享,但定義靜态全局變量還有以下好處:

靜态全局變量不能被其它檔案所用;(好像是差別extern的)

其它檔案中可以定義相同名字的變量,不會發生沖突;

您可以将上述示例代碼改為如下:

//Example 2 
//File1 
#include <iostream.h> 
void fn(); 
static int n; //定義靜态全局變量(隻能在本檔案中使用) 
void main() 
{ 
n=; 
cout < <n < <endl; 
fn(); 
} 

//File2 
#include <iostream.h> 
extern int n;(可在别的檔案中引用這個變量) 
void fn() 
{ 
n++; 
cout < <n < <endl; 
}
           

編譯并運作Example2,您就會發現上述代碼可以分别通過編譯,但link時出現錯誤。

試着将 static int n; //定義靜态全局變量

改為

int n; //定義全局變量

再次編譯運作程式,細心體會全局變量和靜态全局變量的差別。

2、靜态局部變量

定義:在局部變量前加上static關鍵字時,就定義了靜态局部變量。我們先舉一個靜态局部變量的例子,如下://Example 3

#include <iostream.h> 
void fn(); 
void main() 
{ 
fn(); 
fn(); 
fn(); 
} 
void fn() 
{ 
static n=; 
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=; 
cout < <n < <endl; 
}
           

定義靜态函數的好處:靜态函數不能被其它檔案所用;

其它檔案中可以定義相同名字的函數,不會發生沖突;

二、面向對象的static關鍵字(類中的static關鍵字)

1、靜态資料成員

在類内資料成員的聲明前加上關鍵字static,該資料成員就是類内的靜态資料成員。

先舉一個靜态資料成員的例子。

//Example  
#include <iostream.h> 
class Myclass 
{ 
public: 
Myclass(int a,int b,int c); 
void GetSum(); 

private: 
int a,b,c; 
static int Sum;//聲明靜态資料成員!!!!!!!!!!!!!!!  
}; 
int Myclass::Sum=;//定義并初始化靜态資料成員!!!!!!!! 

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(,,); 
M.GetSum(); 
Myclass N(,,); 
N.GetSum(); 
M.GetSum(); 

}
           

可以看出,靜态資料成員有以下特點:

對于非靜态資料成員,每個類對象都有自己的拷貝。而靜态資料成員被當作是類的成員。無論這個類的對象被定義了多少個,靜态資料成員在程式中也隻有一份拷貝,由該類型的所有對象共享通路。也就是說,靜态資料成員是該類的所有對象所共有的。對該類的多個對象來說,靜态資料成員隻配置設定一次記憶體,供所有對象共用。是以,靜态資料成員的值對每個對象都是一樣的,它的值可以更新;

靜态資料成員存儲在全局資料區。靜态資料成員定義時要配置設定空間,是以不能在類聲明中定義。在Example 5中,語句

int Myclass::Sum=0;

是定義靜态資料成員;

靜态資料成員和普通資料成員一樣遵從public,protected,private通路規則;

因為靜态資料成員在全局資料區配置設定記憶體,屬于本類的所有對象共享,是以,它不屬于特定的類對象,在沒有産生類對象時其作用域就可見,即在沒有産生類的執行個體時,我們就可以操作它;

靜态資料成員初始化與一般資料成員初始化不同。

靜态資料成員初始化的格式為:

==<資料類型><類名>::<靜态資料成員名>=<值>==

類的靜态資料成員有兩種通路形式:

==<類對象名>.<靜态資料成員名> 或<類類型名>::<靜态資料成員名>==

如果靜态資料成員的通路權限允許的話(即public的成員),可在程式中,按上述格式來引用靜态資料成員 ;

靜态資料成員主要用在各個對象都有相同的某項屬性的時候。比如對于一個存款類,每個執行個體的利息都是相同的。是以,應該把利息設為存款類的靜态資料成員。

這有兩個好處,第一,不管定義多少個存款類對象,利息資料成員都共享配置設定在全局資料區的記憶體,是以節省存儲空間。

第二,一旦利息需要改變時,隻要改變一次,則所有存款類對象的利息全改變過來了;

同全局變量相比,使用靜态資料成員有兩個優勢:

靜态資料成員沒有進入程式的全局名字空間,是以不存在與程式中其它全局名字沖突的可能性;

可以實作資訊隐藏。靜态資料成員可以是private成員,而全局變量不能;

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=;//定義并初始化靜态資料成員 

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是非靜态資料成員 靜态成員函數由于不是與任何的對象相聯系,是以它不具有this指針。從這個意義上講,它無法通路屬于類對象的非靜态資料成員,也無法通路非靜态成員函數,它隻能調用其餘的靜态成員函數。
cout < <"Sum=" < <Sum < <endl; 
} 

void main() 
{ 
Myclass M(,,); 
M.GetSum(); 
Myclass N(,,); 
N.GetSum(); 
Myclass::GetSum(); 
} 
           

關于靜态成員函數,可以總結為以下幾點:

出現在類體外的函數定義不能指定關鍵字static;

靜态成員之間可以互相通路,包括靜态成員函數通路靜态資料成員和通路靜态成員函數;

非靜态成員函數可以任意地通路靜态成員函數和靜态資料成員;

靜态成員函數不能通路非靜态成員函數和非靜态資料成員;

由于沒有this指針的額外開銷,是以靜态成員函數與類的全局函數相比速度上會有少許的增長;

調用靜态成員函數,可以用成員通路操作符(.)和(->)為一個類的對象或指向類對象的指針調用靜态成員函數.

繼續閱讀