C++ class中的靜態(static)成員
(1)?????? 靜態數據成員
? ?①一般地靜態數據成員在該類定義之外被初始化,如同一個成員函數被定義在類定義之外一樣。在這種定義中的靜態成員的名字必須被其類名限定修飾,例如下面是_interestRate的初始化
// 靜態類成員的顯式初始化
#include "account.h"
double Account::_interestRate = 0.0589;
?
? ?②靜態數據成員的初始化不應該被放在頭文件中,而應該放在含有類的非inline 函數定義的文件中。靜態數據成員可以被聲明為任意類型。例如:
?
#include <string>
class Account
{
// ...
private:
??? static const string name;
};
const string Account::name( "Savings Account" );
?
作為特例,整型的const 靜態數據成員可以在類體中用一常量值初始化。例如,如果決定用一個字符數組而不是string 來存儲賬戶的姓名那么我們可以用int 型的const 數據成員指定該數組的長度,例如:
?
// 頭文件
class Account {
// ...
private:
static const int nameSize = 16;
static const char name[nameSize];
};
// 文本文件
const int Account::nameSize; // 必需的成員定義
const char Account::name[nameSize] = "Savings Account";
?
用常量值作初始化整型的const 靜態數據成員是一個常量表達式(constant expression)。如果需要在類體中使用這個被命名的值,那么,類設計者可聲明這樣的靜態數據成員。例如,因為const 靜態數據成員nameSize是一個常量表達式,所以類的設計者可以用它來指定數組數據成員name 的長度。在類體內初始化一個const 靜態數據成員時,該成員必須仍然要被定義在類定義之外。但是因為這個靜態數據成員的初始值是在類體中指定的所以在類定義之外的定義不能指定初始值。
因為name 是一個數組,不是整型,所以它不能在類體內被初始化。任何試圖這么做的行為都會導致編譯時刻錯誤,例如:
?
class Account {
// ...
private:
static const int nameSize = 16; // ok: 整型
static const char name[nameSize] =
"Savings Account"; // 錯誤
};
name 必須在類定義之外被初始化。
這個例子還說明了一點我們注意到成員nameSize 指定了數組name 的長度,而數組name的定義出現在類定義之外
const char Account::name[nameSize] = "Savings Account";
nameSize 沒有被類名Account 限定修飾。盡管nameSize 是私有成員,但是name的定義仍沒有錯。如同類成員函數的定義可以引用類的私有成員一樣,靜態數據成員的定義也可以。靜態數據成員name的定義是在它的類的域內,當限定修飾名Account::name被看到之后,它就可以引用Account 的私有數據。
?
③靜態數據成員的訪問
1.???? 在類的成員函數中可以直接訪問該類的靜態數據成員,而不必使用成員訪問操作符:
inline double Account::dailyReturn()
{
??? return( _interestRate / 365 * _amount );
}
2.???? 在非成員函數中,我們必須以如下兩種方式訪問靜態數據成員:
?? I. 使用成員訪問操作符
class Account {
// ...
private:
friend int compareRevenue( Account& , Account* );
// 余下部分未變
};
// 引用和指針參數來說明對象和指針訪問
int compareRevenue( Account &ac1, Account *ac2 )
{
double ret1, ret2;
ret1 = ac1._interestRate * ac1._amount;
ret2 = ac2->_interestRate * ac2->_amount;
// ...
}
ac1._interestRate 和ac2->_interestRate都引用靜態成員Account::_interestRate
????
????? II. 訪問靜態數據成員的另一種方法,是用被類名限定修飾的名字直接訪問它
// 用限定修飾名訪問靜態成員
if ( Account:_interestRate < 0.05 )
當我們不通過類的成員訪問操作符訪問靜態數據成員時必須指定類名以及緊跟其后的域操作符????????Account:: 。
因為靜態成員不是全局對象所以我們不能在全局域中找到它下面的friend 函數compareRevenue()的定義與剛剛給出的等價
?
int compareRevenue( Account &ac1, Account *ac2 )
{
double ret1, ret2;
ret1 = Account::_interestRate * ac1._amount;?? //因為是friend函數,才可以訪
//私有的靜態成員變量_interestRate
ret2 = Account::_interestRate * ac2->_amount;
// ...
}
注意:靜態數據成員的“惟一性”本質(獨立于類的任何對象而存在的惟一實例),使它能夠以獨特的方式被使用,這些方式對于非static 數據成員來說是非法的
①靜態數據成員的類型可以是其所屬類,而非static 數據成員只能被聲明為該類的對象的指針或引用。例如:
class Bar {
public:
// ...
private:
static Bar mem1; // ok
Bar *mem2; // ok
Bar mem3; // 錯誤
};
2 靜態數據成員可以被作為類成員函數的缺省實參,而非static 成員不能。例如:
extern int var;
class Foo {
private:
int var;
static int stcvar;
public:
// 錯誤: 被解析為非 static 的 Foo::var
// 沒有相關的類對象
int mem1( int = var );
?
// ok: 解析為 static 的 Foo::stcvar
// 無需相關的類對象
int mem2( int = stcvar );
?
// ok: int var 的全局實例
int mem3( int = ::var );
};
?
(2)?????? 靜態成員函數
??? 使用類的靜態成員函數來訪問類的私有靜態數據成員。
??? 靜態成員函數的聲明除了在類體中的函數聲明前加上關鍵字static, 以及不能聲明為const 或volatile之外,與非靜態成員函數相同。出現在類體外的函數定義不能指定關鍵字static。
靜態成員函數沒有this 指針,因此在靜態成員函數中,隱式或顯式地引用這個指針都將導致編譯時刻錯誤。試圖訪問隱式引用this 指針的非靜態數據成員也會導致編譯時刻錯誤。例如前面給出的成員函數dailyReturn()就不能被聲明為靜態成員函數,因為它訪問了非靜態數據成員amount。
我們可以用成員訪問操作符點. 和箭頭-> 為一個類對象或指向類對象的指針調用靜態成員函數,也可以用限定修飾名直接訪問或調用靜態成員函數,而無需聲明類對象。
?
(3)在派生類中的靜態數據成員與靜態成員函數
class的static data member只有一份實例,被class和class的派生類的所有實例共享。class和class的派生類共用同一塊內存中的靜態數據成員。
一個簡單的例子:
#include <iostream>
using namespace std;
?
class A{
public:
???? A(){}
???? ~A(){}
???? static void SetA(int b){a=b;}
???? static int GetA(){return a;}
private:
???? static int a;
};
int A::a = 5;
class B : public A{
public:
???? B(){}
???? ~B(){}
};
int main(){
cout<< "a=" << A::GetA() <<'/n';? //輸出5
cout<< "a=" << B::GetA() <<'/n'; ?//輸出5
B::SetA(4);
cout<< "a=" << B::GetA() <<'/n';? //輸出4??
cout<< "a=" << A::GetA() <<'/n';? //輸出4
return 0;
}??
類的靜態函數與函數中的靜態數據成員在編譯期綁定!