日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++中const、volatile、mutable的用法

發(fā)布時(shí)間:2023/12/9 c/c++ 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++中const、volatile、mutable的用法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

From: http://blog.csdn.net/wuliming_sc/article/details/3717017

?

const修飾普通變量和指針

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

const TYPE value;

TYPE const value;

這兩種寫法在本質(zhì)上是一樣的。它的含義是:const修飾的類型為TYPE的變量value是不可變的。對(duì)于一個(gè)非指針的類型TYPE,無論怎么寫,都是一個(gè)含義,即value值不可變。例如:

const int nValue ?? //nValueconst

int const nValue??? //nValueconst

但是對(duì)于指針類型的TYPE,不同的寫法會(huì)有不同情況:

l? 指針本身是常量不可變

(char*) const pContent;

l? 指針?biāo)赶虻膬?nèi)容是常量不可變

const (char) *pContent;

(char) const *pContent;

l? 兩者都不可變

const char* const pContent;

識(shí)別const到底是修飾指針還是指針?biāo)傅膶?duì)象,還有一個(gè)較為簡便的方法,也就是沿著*號(hào)劃一條線:

如果const位于*的左側(cè),則const就是用來修飾指針?biāo)赶虻淖兞?#xff0c;即指針指向?yàn)槌A?#xff1b;

如果const位于*的右側(cè),const就是修飾指針本身,即指針本身是常量。

const修飾函數(shù)參數(shù)

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

void function(const int Var); ????//傳遞過來的參數(shù)在函數(shù)內(nèi)不可以改變(無意義,該函數(shù)以傳值的方式調(diào)用)

void function(const char* Var); ??//參數(shù)指針?biāo)竷?nèi)容為常量不可變

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

void function(const Class& Var); //引用參數(shù)在函數(shù)內(nèi)不可以改變

參數(shù)const通常用于參數(shù)為指針或引用的情況,若輸入?yún)?shù)采用“值傳遞”方式,由于函數(shù)將自動(dòng)產(chǎn)生臨時(shí)變量用于復(fù)制該參數(shù),該參數(shù)本就不需要保護(hù),所以不用const修飾。

const修飾類對(duì)象/對(duì)象指針/對(duì)象引用

const修飾類對(duì)象表示該對(duì)象為常量對(duì)象,其中的任何成員都不能被修改。對(duì)于對(duì)象指針和對(duì)象引用也是一樣。

const修飾的對(duì)象,該對(duì)象的任何非const成員函數(shù)都不能被調(diào)用,因?yàn)槿魏畏?/span>const成員函數(shù)會(huì)有修改成員變量的企圖。

例如:

class AAA

{
?? void func1();

void func2() const;

}

const AAA aObj;

aObj.func1(); 錯(cuò)誤

aObj.func2(); 正確

?

const AAA* aObj = new AAA();

aObj->func1(); 錯(cuò)誤

aObj->func2(); 正確

const修飾數(shù)據(jù)成員

const數(shù)據(jù)成員只在某個(gè)對(duì)象生存期內(nèi)是常量,而對(duì)于整個(gè)類而言卻是可變的。因?yàn)轭惪梢詣?chuàng)建多個(gè)對(duì)象,不同的對(duì)象其const數(shù)據(jù)成員的值可以不同。所以不能在類聲明中初始化const數(shù)據(jù)成員,因?yàn)轭惖膶?duì)象未被創(chuàng)建時(shí),編譯器不知道const 數(shù)據(jù)成員的值是什么,例如:
class A
{
???
const int size = 100; //錯(cuò)誤
???
int array[size];????? ?//錯(cuò)誤,未知的size
}
const
數(shù)據(jù)成員的初始化只能在類的構(gòu)造函數(shù)的初始化列表中進(jìn)行。要想建立在整個(gè)類中都恒定的常量,可以用類中的枚舉常量來實(shí)現(xiàn),例如:

class A
{


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


}
枚舉常量不會(huì)占用對(duì)象的存儲(chǔ)空間,他們?cè)诰幾g時(shí)被全部求值。但是枚舉常量的隱含數(shù)據(jù)類型是整數(shù),其最大值有限,且不能表示浮點(diǎn)數(shù)。

const修飾成員函數(shù)

const修飾類的成員函數(shù),用const修飾的成員函數(shù)不能改變對(duì)象的成員變量。一般把const寫在成員函數(shù)的最后:

class A

{

?? …

void function()const; //常成員函數(shù), 它不改變對(duì)象的成員變量. 也不能調(diào)用類中任何非const成員函數(shù)。

}

對(duì)于const類對(duì)象/指針/引用,只能調(diào)用類的const成員函數(shù)。

const修飾成員函數(shù)的返回值

1、一般情況下,函數(shù)的返回值為某個(gè)對(duì)象時(shí),如果將其聲明為const時(shí),多用于操作符的重載。通常,不建議用const修飾函數(shù)的返回值類型為某個(gè)對(duì)象或?qū)δ硞€(gè)對(duì)象引用的情況。原因如下:如果返回const對(duì)象,或返回const對(duì)象的引用,則返回值具有const屬性,返回實(shí)例只能訪問類A中的公有(保護(hù))數(shù)據(jù)成員和const成員函數(shù),并且不允許對(duì)其進(jìn)行賦值操作,這在一般情況下很少用到。

2
、如果給采用指針傳遞方式的函數(shù)返回值加const修飾,那么函數(shù)返回值(即指針?biāo)傅膬?nèi)容)不能被修改,該返回值只能被賦給加const 修飾的同類型指針:

const char * GetString(void);
如下語句將出現(xiàn)編譯錯(cuò)誤:
char *str=GetString();
正確的用法是:
const char *str=GetString();
3
、函數(shù)返回值采用引用傳遞的場合不多,這種方式一般只出現(xiàn)在類的賻值函數(shù)中,目的是為了實(shí)現(xiàn)鏈?zhǔn)奖磉_(dá)。如:
class A
{


???
A &operate= (const A &other); //賦值函數(shù)

}
A a,b,c; //a,b,c
A的對(duì)象

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

const常量與define宏定義的區(qū)別

l? 編譯器處理方式不同

define宏是在預(yù)處理階段展開。

const常量是編譯運(yùn)行階段使用。

l? 類型和安全檢查不同

define宏沒有類型,不做任何類型檢查,僅僅是展開。

const常量有具體的類型,在編譯階段會(huì)執(zhí)行類型檢查。

l? 存儲(chǔ)方式不同

define宏僅僅是展開,有多少地方使用,就展開多少次,不會(huì)分配內(nèi)存。

const常量會(huì)在內(nèi)存中分配(可以是堆中也可以是棧中)

volatile關(guān)鍵字

volatile的本意是“易變的”,volatile關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如操作系統(tǒng)、硬件或者其它線程等。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的穩(wěn)定訪問。

當(dāng)要求使用volatile 聲明的變量的值的時(shí)候,系統(tǒng)總是重新從它所在的內(nèi)存讀取數(shù)據(jù),即使它前面的指令剛剛從該處讀取過數(shù)據(jù)。而且讀取的數(shù)據(jù)立刻被寄存。例如:

volatile int i=10;

int a = i;

。。。//其他代碼,并未明確告訴編譯器,對(duì)i進(jìn)行過操作

int b = i;

volatile 指出 i是隨時(shí)可能發(fā)生變化的,每次使用它的時(shí)候必須從i的地址中讀取,因而編譯器生成的匯編代碼會(huì)重新從i的地址讀取數(shù)據(jù)放在b中。而優(yōu)化做法是,由于編譯器發(fā)現(xiàn)兩次從i讀數(shù)據(jù)的代碼之間的代碼沒有對(duì)i進(jìn)行過操作,它會(huì)自動(dòng)把上次讀的數(shù)據(jù)放在b中。而不是重新從i里面讀。這樣以來,如果i是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)就容易出錯(cuò),所以說volatile可以保證對(duì)特殊地址的穩(wěn)定訪問。

?

注意,在vc6中,一般調(diào)試模式?jīng)]有進(jìn)行代碼優(yōu)化,所以這個(gè)關(guān)鍵字的作用看不出來。下面通過插入?yún)R編代碼,測試有無volatile關(guān)鍵字,對(duì)程序最終代碼的影響。首先用classwizard建一個(gè)win32 console工程,插入一個(gè)voltest.cpp文件,輸入下面的代碼:

#include <stdio.h>

void main()

{

int i=10;

int a = i;

?

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

//下面匯編語句的作用就是改變內(nèi)存中i的值,但是又不讓編譯器知道

__asm {

? mov dword ptr [ebp-4], 20h

}

?

int b = i;

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

}

然后,在調(diào)試版本模式運(yùn)行程序,輸出結(jié)果如下:

i = 10

i = 32

然后,在release版本模式運(yùn)行程序,輸出結(jié)果如下:

i = 10

i = 10

?

輸出的結(jié)果明顯表明,release模式下,編譯器對(duì)代碼進(jìn)行了優(yōu)化,第二次沒有輸出正確的i值。下面,我們把 i的聲明加上volatile關(guān)鍵字,看看有什么變化:

#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);

}

分別在調(diào)試版本和release版本運(yùn)行程序,輸出都是:

i = 10

i = 32

這說明這個(gè)關(guān)鍵字發(fā)揮了它的作用!

?

關(guān)于volatile的補(bǔ)充信息:

一個(gè)定義為volatile的變量是說這變量可能會(huì)被意想不到地改變,這樣,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了。精確地說就是,優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地重新讀取這個(gè)變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個(gè)例子:

??? 1). 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)

??? 2). 一個(gè)中斷服務(wù)子程序中會(huì)訪問到的非自動(dòng)變量(Non-automatic variables)

??? 3). 多線程應(yīng)用中被幾個(gè)任務(wù)共享的變量

??? 我認(rèn)為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。嵌入式系統(tǒng)程序員經(jīng)常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內(nèi)容將會(huì)帶來災(zāi)難。假設(shè)被面試者正確地回答了這是問題(嗯,懷疑這否會(huì)是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile的重要性:

??? 1). 一個(gè)參數(shù)既可以是const還可以是volatile嗎?解釋為什么。

??? 2). 一個(gè)指針可以是volatile 嗎?解釋為什么。

??? 3). 下面的函數(shù)有什么錯(cuò)誤:

???????? int square(volatile int *ptr)

???????? {

????????????? return *ptr * *ptr;

???????? }

??? 下面是答案:

??? 1). 是的。一個(gè)例子是只讀的狀態(tài)寄存器。它是volatile因?yàn)樗赡鼙灰庀氩坏降馗淖儭K?/span>const因?yàn)槌绦虿粦?yīng)該試圖去修改它。

??? 2). 是的。盡管這并不很常見。一個(gè)例子是當(dāng)一個(gè)中服務(wù)子程序修該一個(gè)指向一個(gè)buffer的指針時(shí)。

??? 3). 這段代碼的有個(gè)惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個(gè)volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:

??? int square(volatile int *ptr)

??? {

???????? int a,b;

???????? a = *ptr;

? ???????b = *ptr;

???????? return a * b;

???? }

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

???? long square(volatile int *ptr)

???? {

??????????? int a;

??????????? a = *ptr;

??????????? return a * a;

???? }

mutable關(guān)鍵字

mutalbe的中文意思是“可變的,易變的”,跟constant(既C++中的const)是反義詞。在C++中,mutable也是為了突破const的限制而設(shè)置的。被mutable修飾的變量(mutable只能由于修飾類的非靜態(tài)數(shù)據(jù)成員),將永遠(yuǎn)處于可變的狀態(tài),即使在一個(gè)const函數(shù)中。

我們知道,假如類的成員函數(shù)不會(huì)改變對(duì)象的狀態(tài),那么這個(gè)成員函數(shù)一般會(huì)聲明為const。但是,有些時(shí)候,我們需要在const的函數(shù)里面修改一些跟類狀態(tài)無關(guān)的數(shù)據(jù)成員,那么這個(gè)數(shù)據(jù)成員就應(yīng)該被mutalbe來修飾。下面是一個(gè)小例子:

class ClxTest

{

 public:

  void Output() const;

};

?

void ClxTest::Output() const

{

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

}

?

void OutputTest(const ClxTest& lx)

{

 lx.Output();

}

ClxTest的成員函數(shù)Output是用來輸出的,不會(huì)修改類的狀態(tài),所以被聲明為const

函數(shù)OutputTest也是用來輸出的,里面調(diào)用了對(duì)象lxOutput輸出方法,為了防止在函數(shù)中調(diào)用成員函數(shù)修改任何成員變量,所以參數(shù)也被const修飾。

假如現(xiàn)在,我們要增添一個(gè)功能:計(jì)算每個(gè)對(duì)象的輸出次數(shù)。假如用來計(jì)數(shù)的變量是普通的變量的話,那么在const成員函數(shù)Output里面是不能修改該變量的值的;而該變量跟對(duì)象的狀態(tài)無關(guān),所以應(yīng)該為了修改該變量而去掉Outputconst屬性。這個(gè)時(shí)候,就該我們的mutable出場了,只要用mutalbe來修飾這個(gè)變量,所有問題就迎刃而解了。下面是修改過的代碼:

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;

}

計(jì)數(shù)器m_iTimesmutable修飾,那么它就可以突破const的限制,在被const修飾的函數(shù)里面也能被修改。

?

?

?

?

?

?

參考資料:

http://www.cppblog.com/jukevin/archive/2008/12/27/70499.html

http://dev.csdn.net/develop/article/50/50538.shtm

http://www.eb163.com/club/thread-956-1-1.html

http://www.diybl.com/course/3_program/c++/cppjs/200798/70290_4.html

總結(jié)

以上是生活随笔為你收集整理的C++中const、volatile、mutable的用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。