標籤:style blog color 使用 io strong 檔案 資料
有的時候類需要它的一些成員與類本身直接相關,而不是與類的各個對象保持關聯。
聲明靜態成員
我們通過在成員的聲明之前加上關鍵字static使得其與類關聯在一起,和其他成員一樣,靜態成員可以使public得或private的。待用資料成員的類型可以使常量、引用、指標、類類型等。
舉個例子,我們定義一個類,用它表示銀行的賬戶記錄:
class Account{public: void calculate() {amount+=amount*interestRate;} static double rate() {return interestRate;} static void rate(double);private: std::string owner; double amount; static double interestRate; static double initRate();};
類的靜態成員存在於任何對象之外,對象中不包含任何與待用資料成員有關的資料。因此,每個Account對象將包含兩個資料成員:owner和amount。只存在一個interestRate對象而且它被所有所有Account對象共用。
類似的,靜態成員函數也不與任何對象綁定在一起,它們不包含this指標。作為結果,靜態成員函數不能聲明成const的,而且我們也不能在static函數體內使用this指標,這一限制既適用於this的顯式使用,也對調用非靜態成員的隱式使用有效。靜態成員函數不可以調用類的非靜態成員。因為靜態成員函數不含this指標。靜態成員函數不可以同時聲明為 virtual、const、volatile函數 。
class base{ virtual static void func1();//錯誤 static void func2() const;//錯誤 static void func3() 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;
};
待用資料成員函數
靜態成員函數:靜態成員函數一般只能訪問靜態成員變數,如果要訪問非靜態成員變數的話,只能訪問某一個對象的非靜態成員變數和靜態成員函數。可以傳一個對象的指標,引用等參數給這個靜態成員函數。
class a { public: a():m_ia(123) {} int getia() { return m_ia; } static int f(a &aa) { return aa.getia(); } private: int m_ia; }; void main() { a aa; cout<<a::f(aa); }
靜態成員函數中是不能調用非靜態成員的,包括非靜態成員函數和非靜態成員變數。那麼在非靜態成員函數中是否可以調用靜態成員函數呢?答案是肯定的,因為靜 態成員函數屬於類本身,在類的對象產生之前就已經存在了,所以在非靜態成員函數中是可以調用靜態成員函數的。其實,我們也可以以一個記憶體模型這個角度來考 慮,也就是說,無論採取什麼樣的操作,程式碼都是在記憶體中啟動並執行,只有在記憶體中佔有了一席之地,我們才能訪問它。如果一個成員函數或成員變數還沒有在內 存中產生,結果是無法訪問它的。所有靜態成員函數只能訪問靜態成員變數。
使用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。初始值必須是常量運算式,因為這些成員本身就是常量運算式,所以它們能用在所有適用於常量運算式的地方。例如,我們可以用一個初始化了的待用資料成員執行數群組成員的維度:
class Account{public: static double rate() { return interestRate;} static void rate(double);private: static constexpr int period=30; double daily_tbl[period]; //period是常量運算式};
如果在類的內部提供了一個初始值,則成員的定義不能再指定一個初始值了:
//一個不帶初始值的靜態成員的定義
constexpr int Account::period; //初始值在類的定義內提供
即使一個常量待用資料成員在類內部被初始化了,通常情況下也應該在類的外部定義一下該成員。
靜態成員能用於某些情境,而普遍成員不能
如我們所見,靜態成員獨立於任何對象。因為,在某些非待用資料成員可能非法的場合,靜態 成員卻可以正常地使用。舉個例子,待用資料成員可以是不完全類型。特別地,待用資料成員的類型可以就是它所屬的類類型。而非待用資料成員則受到限制,只能聲明成它所屬類的指標或引用。
class Bar{public: //......private: static Bar mem1; //正確:靜態成員可以是不完全類型 Bar *mem2; //正確:指標成員可以使不完全類型 Bar mem3; //錯誤:資料成員必須是完全類型};
靜態成員和普通成員的另一個區別是我們可以使用靜態成員作為預設實參:
class Screen{public: //bkground 不是一個在類中稍後定義的靜態成員 Screen &clear(char =bkground);private: static const char bkground;};
非待用資料成員不能作為預設實參,因為它的值本身屬於對象的一部分,這麼做的結果是無法真正提供一個對象以從中擷取成員的值,最終引發錯誤。