C++中const、volatile、mutable的用法
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; ?? //nValue是const
int const nValue;??? //nValue是const
但是對(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的值可能被意想不到地該變,因此a和b可能是不同的。結(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ì)象lx的Output輸出方法,為了防止在函數(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)該為了修改該變量而去掉Output的const屬性。這個(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_iTimes被mutable修飾,那么它就可以突破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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开方 C语言 迭代法加二分法
- 下一篇: 错误:unrecognized comm