天天看點

C++ 中const和static的差別

對于C/C++語言來講, 

const就是隻讀的意思,隻在聲明中使用; 

static一般有2個作用,規定作用域和存儲方式.對于局部變量,static規定其為靜态存儲方式,每次調用的初始值為上一次調用的值,調用結束後存儲空間不釋放; 

對于全局變量,如果以檔案劃分作用域的話,此變量隻在目前檔案可見;對于static函數也是在目前子產品内函數可見. 

static const 應該就是上面兩者的合集. 

下面分别說明: 

全局: 

const,隻讀的全局變量,其值不可修改. 

static,規定此全局變量隻在目前子產品(檔案)中可見. 

static const,既是隻讀的,又是隻在目前子產品中可見的. 

檔案: 

檔案指針可當作一個變量來看,與上面所說類似. 

函數: 

const,傳回隻讀變量的函數. 

static,規定此函數隻在目前子產品可見. 

類: 

const,一般不修飾類,(在VC6.0中試了一下,修飾類沒啥作用) 

static,C++中似乎沒有靜态類這個說法,一般還是拿類當特殊的變量來看.C#中有靜态類的詳細說明,且用法與普通類大不相同

//

一、const關鍵字

如果把const放在變量類型名前,說明這個變量的值是保持不變的,該變量必須在定義時初始化,初始化後對它進行的任何指派都是非法的。

當指針或者引用指向一個常量時,必須在類型名前使用const辨別這個指針或者引用指向的“變量”為常量,沒有的話就是文法錯誤。如: const int x = 5;      const int* px = &x;     const int& rx = x;這樣一來,直接修改x是不可能的,通過*px或者rx修改x也是不可能的。當然,這個指針還能指向其他的地方,因為指針本身并沒有被辨別為const的。比如,px = &y;

假如變量是一個非常量變量,而指針或者引用的類型名前使用了const,那麼,可以直接修改變量,不能通過指針或者引用修改變量。

如果想讓一個指針本身成為const的,就要在*後加const,即int* const p = &x;這個時候,p就不能再指向其他變量。假如x是非常量的,那它可以通過指針進行修改,因為x并沒有辨別為const。當然,引用天生就是const的,它必須被初始化,而且初始化後就不能再指向新的變量。比如,int x = 5; int& r = x; r = y;第二句代碼不會讓r指向y,而是讓x的值變成了y。

如果在函數接口(參數)中使用const,和在值、指針中使用是類似的。但是,這就更難讓函數傳回指向這個參數對象的指針或者引用了。如果允許的話,客戶代碼就有可能通過别名修改常量。比如,

class Point

{

    int x, y;

public:

    Point closestPointVal(const Point& pt)

    { if( x*x+y*y < pt.x*pt.x+pt.y*pt.y)

        return *this;

    else return pt;}

    Point* closestPointPtr(const Point& pt)

    {   return (x*x+y*y < pt.x*pt.x+pt.y*pt.y) ? this : &pt;}

   Point& closestPointRef(const Point& pt)

    {   return (x*x+y*y < pt.x*pt.x+pt.y*pt.y) ? *this : pt;}

};

第一個函數是傳回值的,不管用不用const,都不會修改實參pt。而第二個函數可以傳回指向pt實參的指針。客戶代碼就可以使用這個指針修改實際參數對象的狀态。同樣,第三個函數傳回的引用也能修改實參對象的狀态。假如這樣的函數是合法的,那麼就有可能誤用,誤用後如果出了錯,很難弄清是這裡出的錯,是以編譯程式直接就認為這樣的函數不合法。也就是,參數對象使用了const,而傳回類型沒有,這是錯誤的。

C++提供了三種方法解決這個問題:1.參數不使用const;2.在成員函數内部,使用const_cast運算符移去const屬性;3.在另外合适的地方添加const。一個真正的程式員是不會放過任何一個機會,将設計人員的設計意圖傳達給客戶代碼程式員和維護人員的。是以,假如某個參數沒有被修改,就要用const來明确說明。而使用const_cast也很糟糕,不易了解。是以隻能使用第三種方法,在函數傳回值前加上const,接收該傳回值的指針或者引用也要是const的。這樣的話,傳回值隻能作為右值,不能作為左值。這樣的傳回值不能調用它所在類的成員函數。

C++提供const關鍵字,不是為了保證一個變量不被修改,而是為了友善編譯程式和維護人員弄清楚一個實體在程式中是否被修改了。如果函數接口中聲明參數為const,我們就認為這個參數不會改變,如果沒有聲明為const,就認為這個參數一定被改變了,而不管函數到底有沒有改變這個參數。

其實,const還有一個含義,就是在函數的參數清單的“)”之後,函數體的“{”之前。這種用法說明函數不會修改目标對象的值。

二、靜态類成員

每建立一個類對象執行個體,都會為該執行個體建立一個單獨的資料成員集合。不管是将對象定義為有名的局部變量,有名的全局變量,或者使用new定義為未命名的動态變量,或者按值傳遞一個對象作為函數參數,或者從一個函數按值傳回一個對象。而對于成員函數,它們的目标代碼卻隻産生一次。因為每個程式函數都有一個隐含的參數,即this指針。

1 用全局變量作為類特性

類為對象執行個體提供了一個藍圖或者說是模闆,類的成員變量都是這個類的所有執行個體的共性,類的每一個執行個體都有一個單獨的副本。但是有時我們需要為一個類的所有執行個體提供共有的資料成員副本,這比在每個類對象中維護單獨的副本能更有效的合理利用記憶體。最常見的例子就是對類的執行個體進行計數。假如這個計數變量count被定義在類中,在構造函數中對其加1,在析構函數中對其減1,似乎能實作計數作用。但是實際上卻是做不到的。因為count是類的成員變量,每個執行個體都有一個count,構造函數對其加1,隻是對自己的count加了1。

使用全局變量就能解決這個問題。但是将它用在大型程式中,存在一些問題,這些問題在前面都講過了。首先在客戶代碼任意地方都可能使用count,增加了子產品之間的依賴性。其次,全局變量名增加了重名機會。最重要的是,count作為全局變量出現時,維護人員不能很好的了解它是做什麼的,除非看注釋或者查閱大量其他代碼。

2 static的含義

static和const一樣很複雜。

首先,将static用于一個全局變量,是說明該全局變量隻對定義在同一個檔案中的函數可見。即使在另一個檔案中使用了extern,另一個檔案中的函數也不能通路這個全局變量。

static的第二個含義是用于一個函數前,說明該函數隻能在同一個檔案中調用。

第三個含義是static用于函數的局部變量。它表明該變量的值不會因為函數終止而消失,它會被儲存下來。再次調用該函數時,這個儲存下來的值會用來初始化該變量。

第四個含義就是用于類的成員變量。它表明對類的所有對象,這個資料成員都隻有一個執行個體。這個執行個體被所有對象共有。static的成員變量可以是private、public、protected的。定義和通路的文法也和其他資料成員一樣。

3 靜态資料成員的初始化

靜态資料成員在類規格說明外部被初始化,這一點和全局變量相似。但是全局變量可以被隐式地初始化為0,而靜态成員變量作為類的資料成員,必須被顯式地初始化,因為類的所有的資料成員都必須被顯式初始化。指派和初始化存在着重要的差別,如果類型名後緊跟着變量名,就是初始化,如果變量名前沒有類型名,就是指派。初始化對于公共的和非公共的靜态資料成員是合法的,但是對非公共的靜态資料成員進行指派卻是不合法的。比如,如果count是private的,那麼Point::count = 0就是錯誤的。

靜态資料成員隻能被初始化一次,是以它應該和其他的成員函數的定義一起放在.cpp檔案中,而不能放在頭檔案中。一個靜态資料成員不能是聯合的成員,也不能是位域的類。聯合和位域都表示屬于某個特定對象的特殊記憶體用途,而靜态資料成員卻不屬于特定對象,它屬于整個類。

4 靜态成員函數

static的第五種含義是用在類的成員函數前,表明這個函數不通路非靜态資料成員,它隻能通路它的參數、類的靜态資料成員、全局變量。注意到靜态成員函數通路的三種類型的資料,都不是描述對象狀态的。而在函數參數清單後面使用const表明該函數不會修改該函數的目标對象的資料成員。是以,一個靜态成員函數沒有必要在參數清單之後添加一個const。

靜态成員函數可以通過對象來調用,也可以直接使用類名來調用。因為它是類的特性,而不是對象的性質。

定義類的某個資料成員為靜态變量,以表明此全局資料邏輯上屬于該類。定義類的成員函數為靜态函數,表明此全局函數邏輯上屬于該類,而該函數隻對靜态資料、全局資料、參數進行操作。

/

使用const。

    這一點在很多經典的關于C 和C++的書籍中是必談的要點。在《Exceptional C++》一書中,對這點有很精彩的描述,現摘錄如下:“沒有正确的安全意識的槍手在世界上是不可能活的很長的。const 觀念不正确的程式員也是一樣和沒有時間戴緊帽子的正确,沒有時間檢查帶電電線的電工一樣不會活的很長。”在C 語言中,const 修飾符表示告訴編譯器此函數将不會改變被修飾的變量的指向的任何值(除了強制類型轉換)。當把指針作為參數傳遞時,總是合适地使用const,不僅可以防止你無意中錯誤的指派,而且還可以防止在作為參數将指針傳遞給函數時可能會修改了本不想改變的指針所指向的對象的值。如: 

const int num= 7;

num = 9; //有/可能得到編譯器的警告。

const char *ptr,則表示該指針所指向的内容不會被改變,如果在程式中被發生對其指派的操作,編譯時将出錯誤提示。如:

const char *ptr = “hello”;

*ptr = 'H';//錯誤,所指内容不可改變也可将const 放在星号後面來聲明指針本身不可改變。如:

char* const ptr;

ptr++; //錯誤,指針本身不可改變

也可同時禁止改變指針和它所引用的内容,其形式如下: const char* const ptr;

使用static

    static 是一個能夠減少命名沖突的有用工具。将隻在一個子產品檔案中的變量和函數使用static 修飾,将不會和其他子產品可能具有相同名稱的函數和變量在子產品連接配接時不會産生名稱沖突。一般來說,隻要不是提供給其它子產品使用的函數,和非全局變量,均應使用static 修飾。将子程式中的變量使用static 修飾時,表示這個變量在程式開始時配置設定記憶體,在程式結束時釋放,它們在程式執行期間保持它們的值。如:

void func1(void)

{

static int time = 0;

time++

}

void func2(void)

{

static int time = 0;

time++;

}

兩個子程式中的time 變量使用static 修飾,是以它們是靜态變量,每調用一次time将進行加1,并保持這個值。它們的功能與下面程式相似:

int time1 = 0;

int time2 = 0;

void func1(void)

{

time1++

}

void func2(void)

{

time2++;

}

我們可以看出,使用static 修飾後,子產品中的全局變量減少,使得程式的更為簡單。

///

const 是常量, 也就是隻讀的, 是一個"右值"

比如 

const int iRet = 2 ;

iRet = 3 ; // 這句是錯的, const類型隻能在初始化的時候指派, 之後不能改變

static稱為靜态變量, static有兩種, 一種是在類的成員變量中, 一種是局部變量或全局變量.

在類中的, 用代碼解釋

class exp {

static int i1 ;

int i2 ;

} ;

int main() {

exp e1 ;

exp e2 ;

e1.i1 = 5 ;

e1.i2 = 10 ;

e2.i1 = 15 ;

e2.i2 = 20 ;

cout << e1.i1 << endl ; //這是輸出的會是15

cout << e1.i2 << endl ; // 這裡輸出是10

return 0 ;

}

類中的static變量的意思是, 在這個類的所有執行個體中的值都是一樣的, 它與const不同, const 隻是隻讀, 在其他方面與不帶const的變量沒什麼差別

還有一種是const全局變量或局部變量, 這兩種是相同的, 局部變量的作用域會和全局變量相同

上代碼:

static int i1 ;

int add(int a, int b) {

static int c = a + b ;

return c ;

}

void main() {

add(5, 8); 

int result = c ; //這裡 result會是13 無論static變量 是全局的還是局部的, 作用域都和全局變量相同

cout<<result<<endl;

}

還有幾點static的特點,(适用于static全局變量或局部變量)

1.static變量在全局資料區配置設定記憶體, 也就是 在生成的可執行檔案中占一定空間, 這點與全局變量相同

2.static變量隻在它所在的源檔案中可用. 如果一個項目有很多 .c或者.cpp檔案, static變量可以出現在每一個檔案中, 而他們卻是不同的, static可以解決許多檔案中變量 名字相同的問題.