(1) 初始化在類體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或?qū)ο笙嗷煜?/div>
(2) 初始化時(shí)不加該成員的訪問權(quán)限控制符private,public等。
(3) 初始化時(shí)使用作用域運(yùn)算符來標(biāo)明它所屬類,因此,靜態(tài)數(shù)據(jù)成員是類的成員,而不是對(duì)象的成員。
3、靜態(tài)數(shù)據(jù)成員是靜態(tài)存儲(chǔ)的,它是靜態(tài)生存期,必須對(duì)它進(jìn)行初始化。
4、引用靜態(tài)數(shù)據(jù)成員時(shí),采用如下格式:
<類名>::<靜態(tài)成員名>
如果靜態(tài)數(shù)據(jù)成員的訪問權(quán)限允許的話(即public的成員),可在程序中,按上述格式來引用靜態(tài)數(shù)據(jù)成員。
靜態(tài)成員函數(shù)
靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員一樣,它們都屬于類的靜態(tài)成員,它們都不是對(duì)象成員。因此,對(duì)靜態(tài)成員的引用不需要用對(duì)象名。
在靜態(tài)成員函數(shù)的實(shí)現(xiàn)中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時(shí),可通過對(duì)象來引用。
下面看一個(gè)例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
}
};
void main( void )
{
Point pt;
pt.init();
pt.output();
}
這樣編譯是不會(huì)有任何錯(cuò)誤的。
下面這樣看
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
}
};
void main( void )
{
Point::output();
}
這樣編譯會(huì)處錯(cuò),錯(cuò)誤信息:illegal call of non-static member function,為什么?
因?yàn)樵跊]有實(shí)例化一個(gè)類的具體對(duì)象時(shí),類是沒有被分配內(nèi)存空間的。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
}
};
void main( void )
{
Point::init();
}
這時(shí)編譯就不會(huì)有錯(cuò)誤,因?yàn)樵陬惖亩x時(shí),它靜態(tài)數(shù)據(jù)和成員函數(shù)就有了它的內(nèi)存區(qū),它不屬于類的任何一個(gè)具體對(duì)象。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
int x;
int y;
};
void main( void )
{
Point::init();
}
編譯出錯(cuò):
illegal reference to data member 'Point::x' in a static member function
illegal reference to data member 'Point::y' in a static member function
在一個(gè)靜態(tài)成員函數(shù)里錯(cuò)誤的引用了數(shù)據(jù)成員,
還是那個(gè)問題,靜態(tài)成員(函數(shù)),不屬于任何一個(gè)具體的對(duì)象,那么在類的具體對(duì)象聲明之前就已經(jīng)有了內(nèi)存區(qū),
而現(xiàn)在非靜態(tài)數(shù)據(jù)成員還沒有分配內(nèi)存空間,那么這里調(diào)用就錯(cuò)誤了,就好像沒有聲明一個(gè)變量卻提前使用它一樣。
也就是說在靜態(tài)成員函數(shù)中不能引用非靜態(tài)的成員變量。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
x = 0;
y = 0;
init();
}
static void init()
{
}
private:
int x;
int y;
};
void main( void )
{
Point::init();
}
好的,這樣就不會(huì)有任何錯(cuò)誤。這最終還是一個(gè)內(nèi)存模型的問題,
任何變量在內(nèi)存中有了自己的空間后,在其他地方才能被調(diào)用,否則就會(huì)出錯(cuò)。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
static int x;
static int y;
};
void main( void )
{
Point::init();
}
編譯:
Linking...
test.obj : error LNK2001: unresolved external symbol "private: static int Point::y"
test.obj : error LNK2001: unresolved external symbol "private: static int Point::x"
Debug/Test.exe : fatal error LNK1120: 2 unresolved externals
執(zhí)行 link.exe 時(shí)出錯(cuò).
可以看到編譯沒有錯(cuò)誤,連接錯(cuò)誤,這又是為什么呢?
這是因?yàn)殪o態(tài)的成員變量要進(jìn)行初始化,可以這樣:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
static int x;
static int y;
};
int Point::x = 0;
int Point::y = 0;
void main( void )
{
Point::init();
}
在靜態(tài)成員數(shù)據(jù)變量初始化之后就不會(huì)出現(xiàn)編譯錯(cuò)誤了。
再看看下面的代碼:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
static int x;
static int y;
};
void main( void )
{
}
編譯沒有錯(cuò)誤,為什么?
即使他們沒有初始化,因?yàn)槲覀儧]有訪問x,y,所以編譯不會(huì)出錯(cuò)。
C++會(huì)區(qū)分兩種類型的成員函數(shù):靜態(tài)成員函數(shù)和非靜態(tài)成員函數(shù)。這兩者之間的一個(gè)重大區(qū)別是,靜態(tài)成員函數(shù)不接受隱含的this自變量。所以,它就無法訪問自己類的非靜態(tài)成員。
在某些條件下,比如說在使用諸如pthread(它不支持類)此類的多線程庫時(shí),就必須使用靜態(tài)的成員函數(shù),因?yàn)槠涞刂吠珻語言函數(shù)的地址兼容。這種銅限制就迫使程序員要利用各種解決辦法才能夠從靜態(tài)成員函數(shù)訪問到非靜態(tài)數(shù)據(jù)成員。
第一個(gè)解決辦法是聲明類的所有數(shù)據(jù)成員都是靜態(tài)的。運(yùn)用這種方式的話,靜態(tài)的成員函數(shù)就能夠直接地訪問它們,例如:
class Singleton
{
public:
static Singleton * instance();
private:
Singleton * p;
static Lock lock;
};
Singleton * Singleton::instance()
{
lock.getlock(); // fine, lock is static
if (!p)
p=new Singleton;
lock.unlock();
return p;
}
這種解決方法不適用于需要使用非靜態(tài)數(shù)據(jù)成員的類。
訪問非靜態(tài)數(shù)據(jù)成員
將參照傳遞給需要考量的對(duì)象能夠讓靜態(tài)的成員函數(shù)訪問到對(duì)象的非靜態(tài)數(shù)據(jù):
class A
{
public:
static void func(A & obj);
intgetval() const; //non-static member function
private:
intval;
};
靜態(tài)成員函數(shù)func()會(huì)使用參照obj來訪問非靜態(tài)成員val。
voidA::func(A & obj)
{
int n = obj.getval();
}
將一個(gè)參照或者指針作為靜態(tài)成員函數(shù)的自變量傳遞,就是在模仿自動(dòng)傳遞非靜態(tài)成員函數(shù)里this自變量這一行為。
很喜歡API函數(shù)的那種調(diào)用方法,不論在哪個(gè)類中只要用“::API函數(shù)”就可以調(diào)用了。合理利用靜態(tài)類型(static)可以實(shí)現(xiàn)與此相似的全局變量和全局函數(shù)。
靜態(tài)變量和靜態(tài)函數(shù)有如下性質(zhì):
若在一個(gè)類中用關(guān)鍵字static聲明數(shù)據(jù)成員,則這個(gè)數(shù)據(jù)成員就只存在一個(gè)拷貝,無論該類創(chuàng)建了多少個(gè)實(shí)例,它始終只存在一個(gè),即使該類的實(shí)例一個(gè)也沒創(chuàng)建,它也存在。
若在一個(gè)類中用關(guān)鍵字static聲明函數(shù),該函數(shù)可以用“
類名::函數(shù)名
”方式訪問,無需引用該類的實(shí)例,甚至這個(gè)類的實(shí)例可以不存在。
利用這個(gè)性質(zhì)實(shí)現(xiàn)的全局變量和函數(shù)使用起來很方便。
值得注意的是,全局變量和全局函數(shù)最好集中封裝,不要在文檔、視圖等類內(nèi)部定義,這樣用起來才有全局的感覺。
例:
1、添加一個(gè)沒有基類的新類,設(shè)類名起為CPublic,姑且稱之為公用類
單擊“Insert”菜單下的“New Class”命令,選擇“Class type”為“Generic Class”,在“Name”欄中填入類名“CPublic”,單擊“OK”,則新類建立完畢。
2、包含公用類的頭文件,使各個(gè)類都能訪問它
CPublic的頭文件應(yīng)包含在應(yīng)用程序類的頭文件中,這樣在其它類中引用CPublic類時(shí)就不需要再包含了。
Test.h:(應(yīng)用程序類頭文件)
#include "Public.h" //包含公用類頭文件
class CTestApp : public CWinApp
{
…………
};
3、在公用類中定義全局變量和全局函數(shù),均使用static修飾,靜態(tài)變量還必須在類外定義和初始化
Public.h:(公用類頭文件)
class CPublic
{
public:
CPublic();
virtual ~CPublic();
public:
static int x; //全局變量
static int time; //全局變量
static int f(int y); //全局函數(shù)
…………
}
在公用類中對(duì)靜態(tài)變量進(jìn)行初始化和定義函數(shù)體:
Public.cpp:(公用類程序文件)
int CPublic::x = 0; //初始化全局變量
int CPublic::time; //定義全局變量
CPublic::CPublic()
{
}
CPublic::~CPublic()
{
}
int CPublic::f(int y) //全局函數(shù),這里不要再加static
{
y++;
return y;
}
4、全局量的使用
使用變量:
CPublic::變量名
使用函數(shù):
CPublic::函數(shù)()
如在視圖的某函數(shù)中訪問變量x和函數(shù)f():
void CTestView::xyz()
{
CPublic::x = 0; //訪問變量x
CPublic::time = CPublic::f(1); //訪問函數(shù)f()
…………
}
在其它類中訪問x、time和f()的方法與此相同。
5、幾點(diǎn)注意:
① 由于靜態(tài)量可獨(dú)立于類存在,
不需要生成CPublic類的實(shí)例
。
②
靜態(tài)數(shù)據(jù)成員的定義和初始化必須在類外進(jìn)行
,如例中x的初始化;變量time雖然沒有初始化,但也必須在類外進(jìn)行定義。由于沒有生成CPublic類的實(shí)例,所以它的構(gòu)造函數(shù)和析構(gòu)函數(shù)都不會(huì)被執(zhí)行,在里面做什么工作都沒有什么意義。
③ 如果靜態(tài)函數(shù)需要訪問CPublic類內(nèi)的變量,這些變量也必須為靜態(tài)的。因?yàn)榉庆o態(tài)量在不生成實(shí)例時(shí)都不會(huì)存在。
如:
class CPublic
{
public:
int x; //內(nèi)部變量
static int f(int y) //全局函數(shù)
{
x++;
return x;
};
…………
};
這里x雖為類內(nèi)成員,但如果不生成CPublic類的實(shí)例,就會(huì)出現(xiàn)函數(shù)f()存在,而變量x不存在的問題。
總之,用沒有實(shí)例的類管理全局量是一個(gè)不錯(cuò)的選擇,它具有集中管理,使用方便的好處。當(dāng)然,除非特別必要,全局量還是少用為好,一個(gè)好的編程者決不會(huì)隨意濫用全局量的,一個(gè)封裝做得不好的程序,在修改維護(hù)時(shí)會(huì)讓你吃足苦頭。
靜態(tài)數(shù)據(jù)成員通常應(yīng)該通過非內(nèi)聯(lián)函數(shù)來訪問。
問題:
書上有一段話:如果要通過非靜態(tài)函數(shù)來訪問靜態(tài)數(shù)據(jù)成員,應(yīng)該使用非內(nèi)聯(lián)函數(shù)。同時(shí)我看到的程序如下:
#include <iostream>
using namespace std;
class Point
{
public:
Point(int xx=0,int yy=0) {X=xx;Y=yy;countP++;} Point(Point&p);
~Point(){countP--;}
int GetX(){return X;}
int GetY(){return Y;}
void GetC(){cout<<"Object id="<<countP<<endl;}
private:
int X,Y;
static int countP;
};
Point::Point(Point&p)
{
X=p.X;
Y=p.Y;
countP++;
}
int Point::countP=0;
int main()
{
Point A(4,5);
cout<<"Point A,"<<A.GetX()<<","<<A.GetY();
A.GetC();
Point B(A);
cout<<"Point B,"<<B.GetX()<<","<<B.GetY();
B.GetC();
}
void GetC(){cout<<"Object id="<<countP<<endl;} 中函數(shù)體直接放在類體內(nèi),是一種內(nèi)聯(lián)函數(shù)的隱式聲明,這不是和書上“該使用非內(nèi)聯(lián)函數(shù)”的意思矛盾了?
答:
書上原話:因?yàn)榫幾g器確保在調(diào)用任何一個(gè)非內(nèi)聯(lián)函數(shù)之前,會(huì)初始化靜態(tài)數(shù)據(jù)成員,但是因?yàn)檎{(diào)內(nèi)聯(lián)函數(shù)的時(shí)候,可能沒有初始化靜態(tài)數(shù)據(jù)成員,所以這樣操作(內(nèi)聯(lián)函數(shù)中調(diào)靜態(tài)數(shù)據(jù)成員)是不安全的。
這個(gè)Point::countP使用全局初始化的。
在編譯時(shí)候是在main運(yùn)行之前就已經(jīng)初始化了的。
所以即使寫在內(nèi)聯(lián)函數(shù)里面也沒關(guān)系。
另外,如果沒記錯(cuò)的話,書中寫的應(yīng)該是“最好通過非內(nèi)聯(lián)函數(shù)訪問”
之所以這么做的原因是為了避免內(nèi)聯(lián)成員在為初始化的時(shí)候就被使用了。
但是你看這段程序,全局初始化發(fā)生在main之前,
數(shù)據(jù)直接存儲(chǔ)在EXE的數(shù)據(jù)段了。
所以不會(huì)發(fā)生初始化之前的訪問的。
建議你用斷點(diǎn)看看程序運(yùn)行的順序就知道了。
但是這是針對(duì)這個(gè)程序合理,如果程序大了的話,其他編譯單元調(diào)用靜態(tài)但遠(yuǎn)遠(yuǎn)可能會(huì)發(fā)生對(duì)為初始化變量的訪問。
所以最好還是不要寫在內(nèi)聯(lián)函數(shù)中。
靜態(tài)成員函數(shù)可以直接訪問靜態(tài)數(shù)據(jù)和函數(shù)成員。而訪問非靜態(tài)數(shù)據(jù)成員必須通過參數(shù)傳遞方式得到對(duì)象名,然后通過對(duì)象名來訪問。
class A
{
public:
static void f (A a);
private:
int x;
};
void A::f(A a)
{
cout<<x; //錯(cuò)誤
cout<<a.x;//正確
}