volatile與const綜合分析


在C/C++ 編程中,volatile與const關鍵字一向容易讓人困惑,當然,新手可能從來不用,但是 在高質量和穩健的程序中,這兩個關鍵字 是相當重要的。

    相比const,volatile關鍵字的發展(變化)較少,從C到C++的演變中,一直保持着 它的語義,因此,我們先從volatile來了解下,這兩個關鍵字

一、volatile

1.volatile 的基礎 認知:

volatile 的英文 釋義是 容易 揮發的,

    作為 關鍵字,可以 記憶為 它修飾的 變量 是 不穩定的,可能被其他地方的某些方式改變,因此為了 獲取正確的值,編譯器 不該對其做優化,比如為了 獲取較快的 讀取速度,將它 放入寄存器中等,而是每次都要從它所在的內存中 讀取。

 

BS在 書中 對 volatile 的定義是:

A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.

 

volatile修飾符 意在暗示 編譯器,該被修飾的對象 通過該語言未指定的方式改變他的值,因此,積極的優化都應該被消除。

 

未指定的方式 ,比如 操作系統,硬件或者其他線程等。

    遇到volatile修飾的變量,編譯器對訪問該變量的代碼 不再進行優化,從而可以提供對特殊地質的穩定訪問。

    穩定的訪問的方式 是 ,系統總是重新從該 變量的 內存中讀取數據,即使  它 前面的 指令 剛剛 從該處 讀取過數據。

2.volatile  修飾指針

我們可以使用 volatile修飾 指針,比如

volatile char * myVolatileStr;
char *volatile strVolatilePtr;

  

volatile 修飾 char*  和 *char 是 有較大區別的,和const修飾一樣,volatile可以將其修飾的 內存區域 聲明為 易揮發的,也可以將 指針變量本身聲明為 易揮發的。通常,有以下注意點:

 注意:(1) 可以把一個非volatile int 賦給 volatile int,但是不能把非volatile對象賦給一個volatile對象。

       (2) 除了基本類型外,對用戶定義類型也可以用volatile類型進行修飾。

             (3) C++中一個有volatile標識符的類只能訪問它接口的子集,一個由類的實現者控制的子集。用戶只能用const_cast來獲得對類型接口的完全訪問。此外,volatile向const一樣會從類傳遞到它的成員。

 

3.volatile在 多線程中

 

    在 多線程 中,有些變量是要用volatile關鍵字聲明的。當兩個線程都要用到某一個變量且該變量的值會被改變時,應該用volatile聲明,該關鍵字的作用是防止優化編譯器把變量從內存裝入CPU寄存器中。如果變量被裝入寄存器,那么兩個線程有可能一個使用內存中的變量,一個使用寄存器中的變量,這會造成程序的錯誤執行。volatile的意思是讓編譯器每次操作該變量時一定要從內存中真正取出,而不是使用已經存在寄存器中的值,

 

下面,來對比學習 下 const,

 

二、const

1.const基礎

    在C++中,老手們建議 我們 盡可能的 多使用 const,但是 為啥呢?如果 面試官 問起,你就說,為了程序的穩健性,但是 這和問 鍛煉身體為啥呢,保衛祖國 ,沒什么 區別。

    const 理論上 是 constant的簡寫,constant的英文釋義是  不變的;恆定的;經常的。但是 很多大神將它理解成了 只讀的,readonly,甚至覺得 要把這個關鍵詞 替換成readonly。這在gun編譯器中也是這么 提錯的,很有意思。

    和volatile一樣,const也是對編譯器的約束(廢話),它明確的告訴 編譯器,const修飾的變量 是 不變的,如果出現了 其他地方的對其修飾值的改變,應該在編譯期間就報錯。這樣能大大提高程序的健壯性,當然,對於程序員,在編譯期間就發現錯誤本就是極好的。

2.const修飾局部變量

這是 最基本的用法,如

  

const int i = 5;
int const i = 4;

 

 二者 並無本質差異,都是表示 變量i是 不變的,

 

    

3.const 與指針

     
const char* str;
char * const str;
char const* str;
const char* const str;
const char const* str;

   

以上,聲明了 5種const 與指針的 位置 修飾 關系。
我們一一說明下:
    聲明 1 ,與 const int i,沒有區別 ,修飾 其所指向的 內存區域 是 只讀的,不允許 修改
    聲明 2 ,const 直接修飾 str,即指針變量本身,說明 該指針變量 本身是只讀的,但是,其所指向的內存區域還是可以改變的。
    聲明 3 ,與聲明1 本質一致,見第2
    聲明 4, 聲明1 和聲明 2 的 合並,其意義也是 2者的 合並
    聲明 5 ,錯誤的聲明。
通過以上 分析可以 看出,const與其修飾的哪個 類型近,就限定了哪個為只讀,這是規定,但是要防止像聲明5 那樣的 錯誤聲明,因為有兩個const都在修飾 char,這明顯是錯誤的。

4.const與引用

    在C++中 ,可以使用 const 修飾引用,

5. const修飾函數參數

 
const修飾函數參數是它最廣泛的一種用途,它表示函數體中不能修改參數的值(包括參數本身的值或者參數其中包含的值)。
 
 
  1. void function(constintVar);//傳遞過來的參數在函數內不可以改變(無意義,因為Var本身就是形參)
  2. void function(constchar*Var);//參數指針所指內容為常量不可變
  3. void function(char*constVar);//參數指針本身為常量不可變(也無意義, 因為char* Var也是形參)
 
 
    將參數修飾為常量引用,增加了效率同時防止修改。
修飾引用參數時:
 
 
  1. void function(constClass&Var);//引用參數在函數內不可以改變
  2. void function(const TYPE&Var);//引用參數在函數內為常量不可變
 
 

6. const 修飾函數返回值

 
const修飾函數返回值其實用的並不是很多,它的含義和const修飾普通變量以及指針的含義基本相同。
(1) const int fun1() 這個其實無意義,因為參數返回本身就是賦值。
(2) const int * fun2()
    調用時 const int *pValue = fun2();
    我們可以把fun2()看作成一個變量,那么就是我們上面所說的1.(1)的寫法,即指針內容不可變。
(3) int* const fun3()
調用時 int * const pValue = fun2();
    我們可以把fun2()看作成一個變量,那么就是我們上面所說的1.(2)的寫法,即指針本身不可變。

7. const修飾類對象/對象指針/對象引用

 
const修飾類對象表示該對象為常量對象,其中的任何成員都不能被修改。對於對象指針和對象引用也是一樣。
const修飾的對象,該對象的任何非const成員函數都不能被調用,因為任何非const成員函數會有修改成員變量的企圖
例如:
class AAA
{
   void func1();
void func2()const;
}
const AAA aObj;
aObj.func1();//×
aObj.func2();//正確
 
const AAA* aObj =new AAA();
aObj->func1();// ×
aObj->func2();//正確

 

 
 

8. const修飾成員變量

 
const修飾類的成員函數,表示成員常量,不能被修改,同時它只能在初始化列表中賦值。
 
class A
{
   …
   constint nValue;       //成員常量不能被修改
   …
   A(int x): nValue(x){};//只能在初始化列表中賦值
}

 

 

9. const修飾成員函數

 
const修飾類的成員函數,則該成員函數不能修改類中任何非const成員函數。一般寫在函數的最后來修飾。
 
 
 
class A
{
   …
void function()const;//常成員函數, 它不改變對象的成員變量. 也不能調用類中任何非const成員函數。
}

 

 
對於const類對象/指針/引用,只能調用類的const成員函數,因此,const修飾成員函數的最重要作用就是限制對於const對象的使用。
 

10. const常量與define宏定義的區別

 
(1) 編譯器處理方式不同
    define宏是在預處理階段展開。
    const常量是編譯運行階段使用。
(2) 類型和安全檢查不同
    define宏沒有類型,不做任何類型檢查,僅僅是展開。
    const常量有具體的類型,在編譯階段會執行類型檢查。
(3) 存儲方式不同
    define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。
    const常量會在內存中分配(可以是堆中也可以是棧中)。


三、總結 分析
那么問題來了,理解了 volatile 和 const關鍵詞的使用 場景 ,那么 二者可以同時使用嗎?
比如 const volatile int i;這樣的聲明 是否 有問題呢?
從語義上講,似乎不可能,但是 ,這樣的聲明 其實  是合法的。
    因為const和volatile這兩個類型限定符並不矛盾。const表示(運行時)常量語義:被const修飾的對象在所在的作用域無法進行修改操作,編譯器對於試圖直接修改const對象的表達式會產生編譯錯誤。volatile表示“易變的”,即在運行期對象可能在當前程序上下文的控制流以外被修改(例如多線程中被其它線程修改;對象所在的存儲器可能被多個硬件設備隨機修改等情況):被volatile修飾的對象,編譯器不會對這個對象的操作進行優化。一個對象可以同時被const和volatile修飾,表明這個對象體現常量語義,但同時可能被當前對象所在程序上下文意外的情況修改。







免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM