const限定符

    const是c++的關鍵字之一,被const修飾的都受到強制保護,可以預防意外的變動,提高程序的健壯性。

    我把const的使用情況分爲四種:const對象(變量) ,const引用,const和指針,const和函數,下邊依次介紹。

 

     1.   const對象


      我們經常遇見的使用const修飾變量的目的是爲了防止變量的值被修改:

      例如,定義一個變量bufsize代表一個常數512的時候會有一個嚴重的問題:即bufsize是可以被修改的,它可能被有意無意的修改。const限定符提供了一個解決方法:它把一個對象轉換成一個常量。

       

      定義bufsize爲常量並初始化爲512(因爲常量在定義後就不能被修改,所以定義時必須初始化)。

      變量bufsize仍然是一個左值,但現在這個左值是不可修改的。任何修改bufsize的嘗試都會導致編譯錯誤:

      注意:用常量表達式初始化的const變量,大部分編譯器在編譯是會用相應的常量表達式替換這些const變量的任何使用,所以在實踐中不會有任何存儲空間用於存儲用常量表達式初始化的const變量,當使用const_cast去掉變量的const屬性後,再對變量賦值,會發生如下情況:

     對同一塊內存地址有兩個不同的值,這是因爲const變量t是用常量1來初始化的,在訪問t時,直接用內存中的1替換了。如果不用常量表達式來對t進行初始化,就正常了:

 

      另外一種使用const對象的方法跟變量的作用域有關:

      在全局作用域裏定義非const變量時,它在整個程序中都可以訪問。我們可以把一個非const變量定義在一個文件中,假設已經做了合適的聲明,就可以在另外的文件中使用這個變量:

      //file_1.cpp

      int counter;         //definition

      //file_2.cpp

      extern int counter;     //uses counter from file_1

      ++counter;               //increments counter defined in file_1

 

      與其他變量不同,除非特別說明,在全局作用域聲明的const變量是定義該對象的文件的局部變量。此變量只存在於那個文件中,不能被其他文件訪問。

      通過指定const變量爲extern,就可以在整個程序中訪問const對象:

      //file_1.cpp

     extern const int counter = 0;         //defines and initializes

      //file_2.cpp

      extern const int counter;     //uses counter from file_1

      cout<<counter;               //counter = 0

 

      有時會聲明一個類對象爲const對象。

      如果將某個類對象聲明爲const,則編譯器不允許該對象調用任何可能修改它的成員函數。

      所以在使用這個const對象的接口的時候,要保證不會修改const對象的值,因此需要把調用的接口函數修飾爲const,在函數頭後面加const修飾即可,這就是const函數的用法,僅當一個函數是類成員時,修飾爲const纔有意義。

      如果想要修改成員變量,需要在變量前加mutable 修飾。

 

      2.   const引用

 

      引用是對象的另一個名字,const引用是指向const對象的引用:

         可以讀取但是不能修改refbuf,因此,任何對refbuf的賦值都是不合法的,這個限制的意義是:因爲不能直接對bufsize賦值,所以不能通過使用refbuf來修改bufsize。

       非const引用可以修改其所指向的對象的值,正如ref2,所以將普通的非const引用綁定到const對象上是不合法的。

 

       const引用可以初始化爲不同類型的對象 或者 初始化爲右值,如字面常量:

     

       同樣的初始化對於非const引用卻是不合法的,而且會導致編譯時錯誤。其原因非常微妙啊~

       觀察將引用綁定到不同的類型時所發生的事情:

       double dval = 3.14;

       const int &ri = dval;

       編譯器會把這些代碼轉換成如下形式的編碼:

       int tmp = dval;

       const int &ri = dval;

       如果ri不是const,那麼可以給ri賦一個新的值,這樣做不會修改dval,而是修改了tmp。期望對ri的賦值會修改dval的程序員會發現dval並沒有被修改。僅允許const引用綁定到需要臨時使用的值完全避免了這個問題,因爲const引用是隻讀的。

       非const引用只能綁定到與該引用同類型的對象。

       const引用則可以綁定到不同但是相關的類型的對象或者綁定到右值。

 

     3.   const和指針

       指針和const限定符之間有兩種交互:即指向const對象的指針和const指針。另外還有指向const對象的const指針。

       1. 指向const對象的指針

       可以使用指針來修改它所指向的對象的值,如果指針指向const對象,則不允許用指針來修改其所指的const值。爲了保持這個特性,c++強制要求指向const對象的指針也必須具有const特性:

       const double *cptr;    //cptr may point to a double that is const

       這裏的cptr是一個指向double類型const對象的指針,const限定了cptr指針所指向的對象的類型,而並非cptr本身,即cptr本身不是const,在定義時不需要對它進行初始化。如果需要的話,允許給cptr重新賦值,使其指向另一個const對象,但是不能通過cptr修改其所指對象的值:

 

        把一個const對象的地址賦給一個非const對象的指針也會導致編譯時錯誤:

 

        另外,不能使用void*指針保存const對象的地址,而必須使用const void* 類型的指針保存const對象的地址:

 

        允許把非const對象的地址賦給指向const對象的指針,但不能通過指針修改其值,因爲指向const對象的指針一經定義就不允許修改其所指向的對象的值:

         可以看到,在使用指向const對象的指針時,如果它指向一個非const對象,這個const對象可以通過其他方法改變它的值,導致const指針指向的對象的值也變化了,所以,不能保證指向const的指針所指對象的值一定不可修改。

 

         2.  const指針

         const指針本身的值不能修改:

         int err = 0;

         int *const p = &err;

         const指針p的值不能修改,即不能使p指向其他對象,p需要在定義時初始化。

         const指針所指的對象的值是否能修改取決於該對象的類型,即可以使用const指針修改其所指向的對象的值。

        3.  指向const對象的const指針

        

        既不允許修改pp所指向對象的值,也不允許修改pp的指向。

 

        4.   typedef和指針

        typedef string *pstring;

        const pstring cstr;

 

        cstr的實際類型是: string *const cstr;

 

 

        4.   const和函數

        當一個函數是類的成員函數時,修飾爲const纔有意義。

        對於函數的形參,如果是指針形參:

        指針形參的類型將影響函數調用所使用的實參:可以將指向const型和非const型對象的指針傳遞給指向const對象的指針形參,而不能將指向const對象的指針傳遞給指向非const的指針形參。

        const形參:

        如果函數的形參是非const形參,則既可以傳遞const實參,也可以傳遞非const實參:這是因爲初始化是複製了初始化式的值,所以可以用const對象初始化非const對象,反之亦然。

        如果函數的形參是const形參,則既可以傳遞const實參,也可以傳遞非const實參。

        令人吃驚的是:儘管函數的形參是const,但是編譯器卻將函數的定義視爲其形參被聲明爲普通的非const類型:

        反正編譯器對形參是否是const類型不加區別:

        引用形參:

        我們知道:當需要在函數中修改實參的值,或者需要傳遞大的對象,或者沒辦法實現對象的複製時,要用到引用形參。

        在傳遞大對象時,如果不希望修改對象的值,使用const引用可以避免修改實參。如果引用形參的唯一目的是爲了避免複製實參,則應將形參定義爲const引用。

        當需要修改引用的對象時,函數的形參只是非const引用類型,則非const引用只能與完全相同的非const對象相關聯。這樣就限制了函數的使用,即只能傳遞非const類型的對象,所以應該將不修改相應實參的形參定義爲const引用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章