1. 程式人生 > >const、volatile、mutable的用法 const修飾普通變數和指標

const、volatile、mutable的用法 const修飾普通變數和指標

constvolatilemutable用法

const修飾普通變數和指標

const修飾變數,一般有兩種寫法:

const TYPE value;

TYPE const value;

這兩種寫法在本質上是一樣的。它的含義是:const修飾的型別為TYPE的變數value是不可變的。對於一個非指標的型別TYPE,無論怎麼寫,都是一個含義,即value值不可變。例如:

const int nValue    //nValueconst

int const nValue    //nValueconst

但是對於指標型別的TYPE,不同的寫法會有不同情況:

l  指標本身是常量不可變

(char*) const pContent;

l  指標所指向的內容是常量不可變

const (char) *pContent;

(char) const *pContent;

l  兩者都不可變

const char* const pContent;

識別const到底是修飾指標還是指標所指的物件,還有一個較為簡便的方法,也就是沿著*號劃一條線:

如果const位於*的左側,則const就是用來修飾指標所指向的變數,即指標指向為常量;

如果const位於*的右側,const就是修飾指標本身,即指標本身是常量。

const修飾函式引數

const修飾函式引數是它最廣泛的一種用途,它表示在函式體中不能修改引數的值(包括引數本身的值或者引數其中包含的值)

void function(const int Var);     //

傳遞過來的引數在函式內不可以改變(無意義,該函式以傳值的方式呼叫)

void function(const char* Var);   //引數指標所指內容為常量不可變

void function(char* const Var);   //引數指標本身為常量不可變(也無意義,var本身也是通過傳值的形式賦值的)

void function(const Class& Var); //引用引數在函式內不可以改變

引數const通常用於引數為指標或引用的情況,若輸入引數採用“值傳遞”方式,由於函式將自動產生臨時變數用於複製該引數,該引數本就不需要保護,所以不用const修飾。

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(); 正確

const修飾資料成員

const資料成員只在某個物件生存期內是常量,而對於整個類而言卻是可變的。因為類可以建立多個物件,不同的物件其const資料成員的值可以不同。所以不能在類宣告中初始化const資料成員,因為類的物件未被建立時,編譯器不知道const 資料成員的值是什麼,例如:
class A
{
    const int size = 100; //
錯誤
    int array[size];       //
錯誤,未知的size
}
const
資料成員的初始化只能在類的建構函式的初始化列表中進行。要想建立在整個類中都恆定的常量,可以用類中的列舉常量來實現,例如:

class A
{


enum {size1=100, size2 = 200 };
int array1[size1];
int array2[size2];


}
列舉常量不會佔用物件的儲存空間,他們在編譯時被全部求值。但是列舉常量的隱含資料型別是整數,其最大值有限,且不能表示浮點數。

const修飾成員函式

const修飾類的成員函式,用const修飾的成員函式不能改變物件的成員變數。一般把const寫在成員函式的最後:

class A

{

   …

void function()const; //常成員函式它不改變物件的成員變數也不能呼叫類中任何非const成員函式。

}

對於const類物件/指標/引用,只能呼叫類的const成員函式。

const修飾成員函式的返回值

1、一般情況下,函式的返回值為某個物件時,如果將其宣告為const時,多用於操作符的過載。通常,不建議用const修飾函式的返回值型別為某個物件或對某個物件引用的情況。原因如下:如果返回const物件,或返回const物件的引用,則返回值具有const屬性,返回例項只能訪問類A中的公有(保護)資料成員和const成員函式,並且不允許對其進行賦值操作,這在一般情況下很少用到。 

2
、如果給採用指標傳遞方式的函式返回值加const修飾,那麼函式返回值(即指標所指的內容)不能被修改,該返回值只能被賦給加const 修飾的同類型指標:

const char * GetString(void);
如下語句將出現編譯錯誤:
char *str=GetString();
正確的用法是:
const char *str=GetString();
3
、函式返回值採用引用傳遞的場合不多,這種方式一般只出現在類的賻值函式中,目的是為了實現鏈式表達。如:
class A
{


    A &operate= (const A &other); //
賦值函式

}
A a,b,c; //a,b,c
A的物件

a=b=c;   //
正常
(a=B)=c; //
不正常,但是合法若賦值函式的返回值加const修飾,那麼該返回值的內容不允許修改,上例中a=b=c依然正確。(a=b)=c就不正確了。

const常量與define巨集定義的區別

l  編譯器處理方式不同

define巨集是在預處理階段展開。

const常量是編譯執行階段使用。

l  型別和安全檢查不同

define巨集沒有型別,不做任何型別檢查,僅僅是展開。

const常量有具體的型別,在編譯階段會執行型別檢查。

l  儲存方式不同

define巨集僅僅是展開,有多少地方使用,就展開多少次,不會分配記憶體。

const常量會在記憶體中分配(可以是堆中也可以是棧中)

volatile關鍵字

volatile的本意是“易變的”,volatile關鍵字是一種型別修飾符,用它宣告的型別變量表示可以被某些編譯器未知的因素更改,比如作業系統、硬體或者其它執行緒等。遇到這個關鍵字宣告的變數,編譯器對訪問該變數的程式碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

當要求使用volatile 宣告的變數的值的時候,系統總是重新從它所在的記憶體讀取資料,即使它前面的指令剛剛從該處讀取過資料。而且讀取的資料立刻被寄存。例如:

volatile int i=10;

int a = i;

。。。//其他程式碼,並未明確告訴編譯器,對i進行過操作

int b = i;

volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的彙編程式碼會重新從i的地址讀取資料放在b中。而優化做法是,由於編譯器發現兩次從i讀資料的程式碼之間的程式碼沒有對i進行過操作,它會自動把上次讀的資料放在b中。而不是重新從i裡面讀。這樣以來,如果i是一個暫存器變數或者表示一個埠資料就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。

注意,在vc6中,一般除錯模式沒有進行程式碼優化,所以這個關鍵字的作用看不出來。下面通過插入彙編程式碼,測試有無volatile關鍵字,對程式最終程式碼的影響。首先用classwizard建一個win32 console工程,插入一個voltest.cpp檔案,輸入下面的程式碼:

#include <stdio.h>

void main()

{

int i=10;

int a = i;

printf("i= %d/n",a);

//下面彙編語句的作用就是改變記憶體中i的值,但是又不讓編譯器知道

__asm {

  mov dword ptr [ebp-4], 20h

}

int b = i;

printf("i= %d/n",b);

}

然後,在除錯版本模式執行程式,輸出結果如下:

i = 10

i = 32

然後,在release版本模式執行程式,輸出結果如下:

i = 10

i = 10

輸出的結果明顯表明,release模式下,編譯器對程式碼進行了優化,第二次沒有輸出正確的i值。下面,我們把 i的宣告加上volatile關鍵字,看看有什麼變化:

#include <stdio.h>

void main()

{

volatile int i=10;

int a = i;

printf("i= %d/n",a);

__asm {

  mov dword ptr [ebp-4], 20h

}

int b = i;

printf("i= %d/n",b);

}

分別在除錯版本和release版本執行程式,輸出都是:

i = 10

i = 32

這說明這個關鍵字發揮了它的作用!

關於volatile的補充資訊:

一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份。下面是volatile變數的幾個例子:

    1). 並行裝置的硬體暫存器(如:狀態暫存器)

    2). 一箇中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)

    3). 多執行緒應用中被幾個任務共享的變數

我認為這是區分C程式設計師和嵌入式系統程式設計師的最基本的問題。嵌入式系統程式設計師經常同硬體、中斷、RTOS等等打交道,所用這些都要求volatile變數。不懂得volatile內容將會帶來災難。假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這傢伙是不是直正懂得volatile的重要性:

    1). 一個引數既可以是const還可以是volatile嗎?解釋為什麼。

    2). 一個指標可以是volatile 嗎?解釋為什麼。

    3). 下面的函式有什麼錯誤:

         int square(volatile int *ptr)

         {

              return *ptr * *ptr;

         }

下面是答案:

    1). 是的。一個例子是隻讀的狀態暫存器。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。

    2). 是的。儘管這並不很常見。一個例子是當一箇中服務子程式修該一個指向一個buffer的指標時。

    3). 這段程式碼的有個惡作劇。這段程式碼的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向一個volatile型引數,編譯器將產生類似下面的程式碼:

    int square(volatile int *ptr)

    {

         int a,b;

         a = *ptr;

         b = *ptr;

         return a * b;

     }

由於*ptr的值可能被意想不到地該變,因此ab可能是不同的。結果,這段程式碼可能返不是你所期望的平方值!正確的程式碼如下:

     long square(volatile int *ptr)

     {

            int a;

            a = *ptr;

            return a * a;

     }

mutable關鍵字

mutalbe的中文意思是“可變的,易變的”,跟constant(既C++中的const)是反義詞。在C++中,mutable也是為了突破const的限制而設定的。被mutable修飾的變數(mutable只能由於修飾類的非靜態資料成員),將永遠處於可變的狀態,即使在一個const函式中。

我們知道,假如類的成員函式不會改變物件的狀態,那麼這個成員函式一般會宣告為const。但是,有些時候,我們需要在const的函式裡面修改一些跟類狀態無關的資料成員,那麼這個資料成員就應該被mutalbe來修飾。下面是一個小例子:

class ClxTest

{

public:

void Output() const;

};

void ClxTest::Output() const

{

cout << "Output for test!" << endl;

}

void OutputTest(const ClxTest& lx)

{

lx.Output();

}

ClxTest的成員函式Output是用來輸出的,不會修改類的狀態,所以被宣告為const

函式OutputTest也是用來輸出的,裡面呼叫了物件lxOutput輸出方法,為了防止在函式中呼叫成員函式修改任何成員變數,所以引數也被const修飾。

假如現在,我們要增添一個功能:計算每個物件的輸出次數。假如用來計數的變數是普通的變數的話,那麼在const成員函式Output裡面是不能修改該變數的值的;而該變數跟物件的狀態無關,所以應該為了修改該變數而去掉Outputconst屬性。這個時候,就該我們的mutable出場了,只要用mutalbe來修飾這個變數,所有問題就迎刃而解了。下面是修改過的程式碼:

class ClxTest

{

public:

ClxTest();

~ClxTest();

void Output() const;

int GetOutputTimes() const;

private:

mutable int m_iTimes;

};

ClxTest::ClxTest()

{

m_iTimes = 0;

}

ClxTest::~ClxTest()

{}

void ClxTest::Output() const

{

cout << "Output for test!" << endl;

m_iTimes++;

}

int ClxTest::GetOutputTimes() const

{

return m_iTimes;

}

void OutputTest(const ClxTest& lx)

{

cout << lx.GetOutputTimes() << endl;

lx.Output();

cout << lx.GetOutputTimes() << endl;

}

計數器m_iTimesmutable修飾,那麼它就可以突破const的限制,在被const修飾的函式裡面也能被修改。

相關推薦

constvolatilemutable用法 const修飾普通變數指標

const、volatile、mutable的用法const修飾普通變數和指標const修飾變數,一般有兩種寫法:const TYPE value;TYPE const value;這兩種寫法在本質上是一樣的。它的含義是:const修飾的型別為TYPE的變數value是不可變

轉:C++中constvolatilemutable用法

const修飾普通變數和指標 const修飾變數,一般有兩種寫法: const TYPE value; TYPE const value; 這兩種寫法在本質上是一樣的。它的含義是:const修飾的型別為TYPE的變數value是不可變的。對於一個非指標的型別T

constvolatilemutable用法總結

const、volatile、mutable的用法 const修飾普通變數和指標 const修飾變數,一般有兩種寫法: const TYPE value; TYPE const value; 這兩種寫法在本質上是一樣的。它的含義是:const修飾的型別為TYPE的變數val

嵌入式經典面試題---C中有關預處理,關鍵字constvolatilestatic,位操作,等等

紅色是我標註的重點,綠色是自己新增的內容  C語言測試是招聘嵌入式系統程式設計師過程中必須而且有效的方法。這些年,我既參加也組織了許多這種測試,在這過程中我意識到這些測試能為帶面試者和被面試者提供許多有用資訊,此外,撇開面試的壓力不談,這種測試也是相當有趣的。 從被

多執行緒:synchronizevolatileLock 的區別與用法

Java多執行緒之記憶體可見性和原子性:Synchronized和Volatile的比較       在說明Java多執行緒記憶體可見性之前,先來簡單瞭解一下Java記憶體模型。     

C/C++中儲存型別修飾符的區別(autostaticregisterexternvolatilerestrict)

一、區域性變數和全域性變數: (1)區域性變數:區域性變數也叫自動變數,它宣告在函式開始,生存於棧,它的生命隨著函式的返回而結束。 #include <stdio.h> int main(void) { auto int i = 9; //宣告區域性

Java中的synchronizedvolatileReenTrantLockAtomicXXX

包含 指令 純粹 功能性 title 伸縮 其它 同步問題 留下 多線程和並發性並不是什麽新內容,但是 Java 語言設計中的創新之一就是,它是第一個直接把跨平臺線程模型和正規的內存模型集成到語言中的主流語言。核心類庫包含一個 Thread 類,可以用它來構建、啟動和操縱線

ThreadLocalVolatilesynchronizedAtomic

操作類 分享 使用 實現 數據 輕量級 src dcl 最終 前言 對於ThreadLocal、Volatile、synchronized、Atomic這四個關鍵字,我想一提及到大家肯定都想到的是解決在多線程並發環境下資源的共享問題,但是要細說每一個的特點、區別、應用場景

並發編程之ThreadLocalVolatilesynchronizedAtomic關鍵字掃盲

匯編指令 free clas 保持 break 作用 nta 總結 read 版權聲明:本文為博主原創文章,未經博主允許不得轉載。轉載註明出處:Sunzxyong https://blog.csdn.net/u010687392/article/details/50549

水滴石穿--多執行緒原子操作threadlocalvolatile多執行緒下的單例模式

接著上一篇文章,下面看看幾個比較好理解的知識點!! volatile java關鍵字volatile修飾的變數從字面意義上理解易變的,不穩定的,事實上時告訴編譯器該變數是易變的不要對該變數使用快取等級的優化,每次都從記憶體地址中讀取值。 不過並沒有說明在對volatile修飾的變數進行修

ThreadLocalVolatilesynchronizedAtomic關鍵字掃盲

前言 對於ThreadLocal、Volatile、synchronized、Atomic這四個關鍵字,我想一提及到大家肯定都想到的是解決在多執行緒併發環境下資源的共享問題,但是要細說每一個的特點、區別、應用場景、內部實現等,卻可能模糊不清,說不出個所以然來,所以,本文就對

記憶體屏障volatilefinal

1.     硬體層的記憶體屏障:Load Barrier 和 Store Barrier作用:阻止屏障兩側的指令重排序;      強制把寫緩衝區/快取記憶體中的髒資料等寫回主記憶體,讓快取中相應的資料失效。對於Load Barrier來說,在指令前插入Load Barri

C語言中關鍵字autostaticregisterexternvolatilerestrict的作用

(1):auto關鍵字在C語言中只有一個作用,那就是修飾區域性變數。 (2):auto修飾區域性變數,表示這個區域性變數時自動區域性變數,自動區域性變數分配在棧上。(既然是分配在棧上,說明他如果不初始化的話那麼值就是隨機的....)  (3):平時定義區域性變數時就是定義的auto的,只是省略了auto關鍵字

1-2 (JVM)Java記憶體模型---記憶體可見性重排序順序一致性volatilefinal

一、原子性原子性操作指相應的操作是單一不可分割的操作。例如,對int變數count執行count++d操作就不是原子性操作。因為count++實際上可以分解為3個操作:(1)讀取變數count的當前值;(2)拿count的當前值和1做加法運算;(3)將加完後的值賦給count

.NET多線程之線程安全,Lock(鎖)Monitor(同步訪問)LazyInitializer(延遲初始化)Interlocked(原子操作)static(靜態)構造函數volatile

called val www. queue 多線程 try 退出 con 內存 1、什麽是線程安全    線程安全是編程中的術語,指某個函數、函數庫在多線程環境中被調用時,能夠正確地處理多個線程之間的共享變量,使程序功能正確完成。一般來說,線程安全的函數應該為每個調用它的

java裡的鎖總結(synchronized隱式鎖Lock顯式鎖volatileCAS)

# 一、介紹 首先, java 的鎖分為兩類: 1. 第一類是 **synchronized** 同步關鍵字,這個關鍵字屬於隱式的鎖,是 jvm 層面實現,使用的時候看不見; 2. 第二類是在 jdk5 後增加的 **Lock** 介面以及對應的各種實現類,這屬於顯式的鎖,就是我們能在程式碼層面看

synchronized修飾普通方法靜態方法

對象 -s 修飾 style 安全 安全問題 才會 htable 會有 首先,要知道,synchronized關鍵字修飾普通方法時,獲得的鎖是對象鎖,也就是this。而修飾靜態方法時,鎖是類鎖,也就是類名.class。 synchronized修飾普通方法 Synchron

關於成員內部類不能有static修飾變數方法問題

看了幾篇部落格關於一般內部類不能有static修飾的變數,在這裡記錄一下。問題幾種解釋如下:1.“if you're going to have a static method, the whole inner class has to be static. Without 

c++ constvolatilemutable

const修飾普通變數和指標 const修飾變數,一般有兩種寫法: const TYPE value; TYPE const value; 這兩種寫法在本質上是一樣的。它的含義是:const修飾的型別為TYPE的變數value是不可變的。對於一個非指標的型別TYPE,無論怎麼寫,都是

嵌入式開發中關鍵字conststaticvolatile用法分析

【1】static: 1)限制變數或者函式的作用域 2)設定變數的儲存域 常見的有: (1)static全域性變數(2)static區域性變數(3)static函式 (1)static全域性變數:1、static全域性變數只初使化一次,防止在其他檔案單元中被引用;2、只在定