程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> [原創] 基礎中的基礎(二):C/C++ 中 const 修飾符用法總結

[原創] 基礎中的基礎(二):C/C++ 中 const 修飾符用法總結

編輯:C++入門知識

被修飾的這些東西,具有“只讀”的特點。在編譯的過程中,一旦我們的代碼試圖去改變這些東西,編譯器就應該給出錯誤提示。

盡可能使用const,將幫助我們避免很多錯誤,提高程序正確率。

 

在C/C++中,常見 const 用法有以下幾種:

 

一、const 變量

  debugLevel =   logLevel;                
= ;                    

 

 

              
  main(  argc,  *        debugInfo debug_const1;  
     
      
     debug_not_const.debugLevel =      debug_not_const.line =  
       debugInfo debug_const2 = 
     debug_const2.debugLevel = ;  
     debug_const2.line = ;         
 
       }

 

  在C中,const 結構體變量表示結構體中任何數據域均不允許改變,且需要另一個結構體變量進行初始化。在C++中,struct與class除了默認訪問權限之外,並無本質區別。在下一節進行討論。

二、const 類對象

   const類對象指的是,此類對象不應該被改變。

  • const 類對象與 const 變量並無實質不同,只在於類對象的 “改變” 定義。
  • 類對象的 “改變” 定義
~C SetDebugLevel( debugLevel) { m_debugLevel = PrintDebugLevel() { cout << PrintDebugLevel_const()  { cout << m_debugLevel;};   
 main(  argc,  *= ;       
    debug.SetDebugLevel();       
    debug.PrintDebugLevel();       
    debug.PrintDebugLevel_const(); 

     

  不能改變 const 類對象的任何成員變量,這一點比較好理解,因為 const 本身就帶有不可改變變量取值(內部狀態)的含義。為何const 類成員不能調用非const成員函數呢?我們將在 第九節“const 成員函數” 進行探討。在C++中,struct和class沒有明顯差別,不再贅述。

 

三、指向 const 變量的指針

  指向 const 變量的指針,指的是一個指針,其中保存著一個 const 變量的地址。

  • 由於指針指向一個 const 變量,所以通過指針,不能修改這個 const 變量的值。
  • 雖然指針,但是
  debugLevel =   logLevel =   *p = &= &logLevel;    

*p = ;          

  

  在 *p = 10, 這一句,編譯器是通過“指針的類型”(const int *)還是通過其“指向變量的類型”(const int )來判斷只讀的呢?我們可以通過下面這個小程序來求證一下:

   debugLevel = ;        
  logLevel = ;                
 
   *  * 
 p = &logLevel;                    
 q = (*)&debugLevel;            
 
 *q = ;  
 *p = ;  

  通過10、11行程序的編譯結果,我們可以看出,如果指針的類型為“指向const變量的指針”,即使其指向的內容是非const變量,也無法通過這個指針改變數據內容。反之,如果指針的類型是“指向非const變量的指針”,即使指向的是const變量,也可以通過這個指針改變const變量的內容(稍後討論這一點)。所以,編譯器是通過 “指針的類型” 來判斷是否只讀的。

12 printf(, *, &, q);

   從上邊的說明,我們可以想象,debugLevel的值,被我們通過指針改變了,所以輸出應該是:

debugLevel = 
*q = = = 

  但是,事實上,這個結果是!

 

debugLevel =                      
*q =                               
debugLevel address = =                       

   乍一看,好像同一個地址的東西,采用變量名訪問和采用指針訪問,得到的結果竟然不一樣。其實,之所以產生這種結果,是由於在編譯器變異的過程中,對 const 變量的訪問進行了優化。編譯器將 const 變量直接替換為對應的內容。也就是說,在編譯的過程中 :

printf("debugLevel = %d\n", debugLevel);

 這個語句,被直接替換成了:

printf("debugLevel = %d\n", 10);

 所以,才產生了上邊的現象。當然,這種替換不一定會發生,跟編譯器和優化等級相關。

  上文已經提到了,C++建議使用 const 全局變量來替換一般常量的宏定義。通過這個例子可以看出,使用 const 全局變量之後,由於編譯器會替換其為具體內容,所以在程序實際運行中,並不會產生一次變量訪問,也就使得 const 全局變量和宏定義具有相同的執行效率。同時,使用 const 全局變量,可以讓編譯器幫助我們進行變量類型檢查,提高正確率。

 

  指針也是變量的一種,所以自然有 const 類型指針。

 

四、const 指針

  const指針指的是,一個指針本身經過 const 修飾,自身內容(指針指向)不應該發生改變。

  • 指針的指向一經確定,不能改變。指針指向的內容,可以改變。
 logLevel =  logId =  *  p = & *  q;               

*p = ;                     
p = &logId                  

  指針也是一種變量,只不過其內容保存的是地址而已。所以const指針的內容不能改變,也即是它的指向不能改變。

  const指針和指向const變量的指針,在寫法上容易使人混淆。給大家介紹一種我自己用著比較順手的區分方法:

 

  再比如,const int * p 可以這樣解讀:

  1、const int (* p):變量p是一個指針。

  2、(const int) (* p):(const與就近的 int 結合)這個指針指向 const int 型變量。

  所以,const int * p 是一個指向 const 整形變量的指針。

 

  采用這個方法,相信大家可以自己分辨 int const * p的含義了。

  所以允許像 const int const *p; 這種寫法。在分析這種“錯誤”的寫法時,只要把重復修飾的const忽略即可。

 

五、指向 const 變量的 const 指針

  這種情況是 const 指針和 指向 const 變量的指針的結合,相信大家已經能夠自己分析,不再贅述。

 

六、const 變量作為函數參數

  在函數調用的過程中,函數的參數是建立在函數的棧上的變量。既然是變量,就可以通過 const 進行修飾。

  • 將函數參數聲明為 const 類型,表示對於函數來說,這個參數是一個 const 變量。也就是說,函數內部不能夠改變這個參數的值。
  • 將函數參數是一個指針,把它聲明為 “指向 const 變量的指針” ,。(如果參數聲明的是普通指針,則不允許使用 指向 const 變量的指針 作為參數調用)(與編譯器有關)
 
  OutputInt_const(        a = ;  
     printf(  
 
  OutputInt_not_const(       a = ;  
     printf(  
 
  OutputInt_p_const(   *      *a = ;  
     printf(, *  
 
  OutputInt_p_not_const(  *      *a =      printf(, *  
 
  main(  argc,  *       logLevel =        debugLevel =  
   
   
     OutputInt_p_const(&     OutputInt_p_const(& 
     OutputInt_p_not_const(&     OutputInt_p_not_const(&debugLevel);    
 
       }

 

  為什麼對於指針參數的要求特殊?其實我們可以仔細想一下 const 在修飾參數時的作用。

  首先,函數參數是函數內部可見的一個變量。。對於傳進來的參數,在外邊究竟是什麼樣子的,函數內部並不關心。所以,函數 void OutputInt_const( const int a ) 並不會在意傳入的參數在main函數中是否是只讀的。它只會在函數內部,將入參當作只讀變量處理。

  既然 const 修飾函數參數時,不會限制入參是否為只讀,為什麼 OutputInt_p_not_const( * * 的調用方式有區別呢(見44、45行)?

。所以,只要調用時傳入的參數不是一個指向只讀整形數的指針,就會發生類型不匹配。在示例41行的調用中,使用一個 int * 來調用 OutputInt_p_const 函數,發生類型不匹配,但是 int * 可以隱式轉換為 const int *,所以此處調用可以成功。但在45行中,采用 const int * 來調用需要 int * 的 OutputInt_p_not_const 函數,發生類型不匹配, const int * 不能夠隱式轉換為 int *,所以此處調用失敗。

 

七、const 返回值

  const 型的返回值,指的是函數的返回值為一個 const 變量。

  • 函數返回const返回值,主要用於
 #include <>
 
   
 
  & SetVersion_const( &      versionInfo =        
 
 & SetVersion_not_const( &      versionInfo =        
 
  main(  argc,  *       
     SetVersion_const(versionInfo) = ;      
  
     SetVersion_not_const(versionInfo) = ;  
     
       }

  引用是一個對象的別名,相當於 const 指針,其指向一經確定,就不能改變了。而 const 引用,則相當於指向 const 變量的 const 指針,其指向和指向的內容均不允許改變。所以在函數返回 const 引用時,即便對象本身不是 const 的。在本例中,versionInfo 在 main 函數中不是const的。SetVersion_const 函數返回了一個指向 versionInfo 的 const 引用,不能通過此引用,對 versionInfo 進行修改。

  為什麼會出現這種現象?相信大家都能理解了。請參考 指向 const 變量指針 的相關內容。

  

八、const 成員變量

  const 成員變量指的是類中的成員變量為只讀,不能夠被修改(包括在類外部和類內部)。

  • const 成員變量(在相關構造函數的),
  • 靜態 const 成員變量
         ~ 
               
  
   CDebugModule::m_debugInfo = ;  
 
      : m_debugLevel()  
   
 CDebugModule::~   
  main( argc,  *   
     debugModule.m_debugLevel = ;  
     CDebugModule::m_debugInfo = ; 
 
       }

  類對象的實例化過程可以理解為包含以下步驟:首先,開辟整個類對象的內存空間。之後,根據類成員情況,分配各個成員變量的內存空間,並通過構造函數的初始化列表進行初始化。最後,執行構造函數中的代碼。由於 const 成員變量必須在定義(分配內存空間)時,就進行初始化。所以需要在夠在函數的初始化列表中初始化。

  靜態成員變量並不屬於某個類對象,而是整個類共有的。靜態成員變量可以不依附於某個實例化後的類對象進行訪問。那麼,(否則,只有實例化至少一個對象,才能訪問靜態成員)。所以,靜態成員變量不能夠由構造函數進行內存分配,而應該在類外部單獨定義,在實例化任何對象之前,就開辟好空間。又由於 const 成員變量 必須初始化,所以靜態成員變量必須在定義的時候就初始化。

 

 九、const 成員函數

   const成員函數指的是,此函數不應該修改任何成員變量。

  • 傳給const成員函數的this指針,是
  • const成員函數,不能夠修改任何成員變量,除非成員變量被 mutable 修飾符修飾。
         ~ 
       m_debugLevel_not_mutable;        
     mutable  m_debugLevel_mutable;    
 
       SetDebugLevel_not_const( debugLevel);   
      SetDebugLevel_const( debugLevel) ; 
   
  CDebugModule::SetDebugLevel_not_const(      m_debugLevel_not_mutable =     m_debugLevel_mutable =       
  CDebugModule::SetDebugLevel_const( debugLevel) 
      m_debugLevel_not_mutable = debugLevel; 
     m_debugLevel_mutable = debugLevel;     
       
  main( argc,  *   
     debugModule.SetDebugLevel_not_const(     debugModule.SetDebugLevel_const( 
       }

 

  在成員函數調用的過程中,都有一個 (可能通過棧,也可能通過CPU寄存器)。這個this指針,(這樣,成員函數才能找到成員變量的地址,從而對其進行操作)。

。也就是說,在const成員函數內部,。通過第二節的探討,相信大家已經能夠明白,為什麼const成員函數不能修改任何成員變量了。

。一般成員函數要求的this指針(別忘了this指針也是一個參數)為:指向對象的const指針。所以此處發生了參數不匹配,無法進行調用。而 const 成員函數要求的this指針,恰恰是 指向const對象的const指針。所以依然能夠調用。

  

 十、總結  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved