天天看點

類的靜态成員

有的時候類需要它的一些成員與類本身直接相關,而不是與類的各個對象保持關聯。

聲明靜态成員

我們通過在成員的聲明之前加上關鍵字static使得其與類關聯在一起,和其他成員一樣,靜态成員可以使public得或private的。靜态資料成員的類型可以使常量、引用、指針、類類型等。

舉個例子,我們定義一個類,用它表示銀行的賬戶記錄:

類的靜态成員存在于任何對象之外,對象中不包含任何與靜态資料成員有關的資料。是以,每個Account對象将包含兩個資料成員:owner和amount。隻存在一個interestRate對象而且它被所有所有Account對象共享。

類似的,靜态成員函數也不與任何對象綁定在一起,它們不包含this指針。作為結果,靜态成員函數不能聲明成const的,而且我們也不能在static函數體内使用this指針,這一限制既适用于this的顯式使用,也對調用非靜态成員的隐式使用有效。靜态成員函數不可以調用類的非靜态成員。因為靜态成員函數不含this指針。靜态成員函數不可以同時聲明為   virtual、const、volatile函數   。

使用類的靜态成員

我們使用作用域運算符直接通路靜态成員:

double r;

r=Account::rate();   //使用作用域運算符通路靜态成員

雖然靜态成員不屬于類的某個對象,但是我們仍然可以使用類的對象、引用或者指針類通路靜态成員:

Account ac1;

Account *ac2=&ac1;

//調用靜态成員函數rate的等價形式

r=ac1.rate();   //通過Account的對象或引用

r=ac2->rate();   //通過指向Account對象的指針

成員函數不要通過作用域運算符就能直接使用靜态成員。

class Account{

public:

  void calculate() {amount+=amount*interestRate;}

private:

  static double interestRate;

};

靜态資料成員函數

  靜态成員函數:靜态成員函數一般隻能通路靜态成員變量,如果要通路非靜态成員變量的話,隻能通路某一個對象的非靜态成員變量和靜态成員函數。可以傳一個對象的指針,引用等參數給這個靜态成員函數。

     靜态成員函數中是不能調用非靜态成員的,包括非靜态成員函數和非靜态成員變量。那麼在非靜态成員函數中是否可以調用靜态成員函數呢?答案是肯定的,因為靜 态成員函數屬于類本身,在類的對象産生之前就已經存在了,是以在非靜态成員函數中是可以調用靜态成員函數的。其實,我們也可以以一個記憶體模型這個角度來考 慮,也就是說,無論采取什麼樣的操作,程式代碼都是在記憶體中運作的,隻有在記憶體中占有了一席之地,我們才能通路它。如果一個成員函數或成員變量還沒有在内 存中産生,結果是無法通路它的。所有靜态成員函數隻能通路靜态成員變量。

使用static關鍵字聲明的函數成員是靜态的,靜态成員函數同樣也屬于整個類,由同一個類的所有對象共同維護,為這些對象所共享.   

      作為成員函數,它的通路屬性可以受到類的嚴格控制,對于公有的靜态函數成員函數,可以通過類名或對象名來調用,但一般情況下建議用對象名來引用靜态函數成員.注意,一般的成員函數隻能通過對象名來調用.   

        由于一個類的靜态成員函數隻有一個拷貝,是以它通路對象的資料和函數時受到了限制.靜态成員函數可以直接通路該類的靜态資料成員.而通路非靜态資料成員, 必須通過參數傳遞方式得到對象名,然後通過對象名來通路.可以看到,通過靜态函數成員通路非靜态成員使相當麻煩的,一般的使用中,它主要用來通路全局變量 或同一個類中的靜态資料成員,特别是和後者一起使用,達到對同一個類中對象之間共享的資料進行維護的目的.   

        構造函數和析構函數不可以定義為static,構造函數要給每一個對象一個this指針如果可以是靜态的,它如何構造和通路this指針?

明顯是不可以的!

      靜态成員變量一般要在.cpp檔案裡進行定義:

  double Account::_interestRate = 0.0589;

  靜态成員函數的聲明除了在類體中的函數聲明前加上關鍵字static 以及不能聲明為const 或volatile 之外與非靜态成員函數相同,出現在類體外的函數定義不能指定關鍵字static.

       const string str = "liangxueliang";

    Account *Acc = new Account(222222,str);

    Acc->getRate();

    Account::getRate();

定義靜态資料成員 

和其他的成員函數一樣,我們既可以在類的内部也可以在類的外部定義靜态成員函數。當在類的外部定義靜态成員時,不能重複static關鍵字,該關鍵字隻出現在類内部的聲明語句:

void Account::rate(double newRate)

{

  interestRate=newRate;

}

和類的所有成員一樣,當我們指向類外部的靜态成員時,必須指明成員所屬的類名。static關鍵字則隻出現在類内部的聲明語句中。

因為靜态資料成員不屬于類的任何一個對象,是以它們并不是在建立類的對象時被定義的。這意味着它們不是由類的構造函數初始化的。而且一般來說,我們不能在類的内部初始化靜态資料成員。相反的,必須在類的外部定義和初始化每個靜态成員。和其他對象一樣,一個靜态資料成員隻能定義一次。

類似于全局變量,靜态資料成員定義在任何函數之外,是以一旦它被定義,就将一直存在于程式的整個生命周期中。

我們定義靜态資料成員的方式和在類的外部定義成員函數差不多。我們需要指定對象的類型名,然後是類名、作用域運算符以及成員自己的名字:

//定義并初始化一個靜态成員

double Account::interestRate=initRate();

這條語句定義了名為interestRate的對象,該對象是類Account的靜态成員,其類型是double。從類名開始,這條定義語句的剩餘部分就都位于類的作用域之内了。是以,我們可以直接使用initRate函數。注意,雖然initRate是私有的,我們也能使用它初始化interestRate。和其他成員的定義一樣,interestRate的定義也可以通路類的私有成員。

要想確定對象隻定義一次,最好的辦法是把靜态資料成員的定義與其他非内聯函數的定義放在同一檔案中。

靜态成員的類内初始化

通常情況下,類的靜态成員不應該在類的内部初始化。然而,我們可以為靜态成員提供const整數類型的類内初始值,不過要求靜态成員必須是字面值常量類型的constexpr。初始值必須是常量表達式,因為這些成員本身就是常量表達式,是以它們能用在所有适用于常量表達式的地方。例如,我們可以用一個初始化了的靜态資料成員執行數組成員的次元:

如果在類的内部提供了一個初始值,則成員的定義不能再指定一個初始值了:

//一個不帶初始值的靜态成員的定義

constexpr int Account::period;  //初始值在類的定義内提供

即使一個常量靜态資料成員在類内部被初始化了,通常情況下也應該在類的外部定義一下該成員。

靜态成員能用于某些場景,而普遍成員不能

如我們所見,靜态成員獨立于任何對象。因為,在某些非靜态資料成員可能非法的場合,靜态 成員卻可以正常地使用。舉個例子,靜态資料成員可以是不完全類型。特别地,靜态資料成員的類型可以就是它所屬的類類型。而非靜态資料成員則受到限制,隻能聲明成它所屬類的指針或引用。

靜态成員和普通成員的另一個差別是我們可以使用靜态成員作為預設實參:

非靜态資料成員不能作為預設實參,因為它的值本身屬于對象的一部分,這麼做的結果是無法真正提供一個對象以從中擷取成員的值,最終引發錯誤。

繼續閱讀