C++基本功:全面掌握const、volatile和mutable關(guān)鍵字
CONST
一.一般應(yīng)用
1.const修飾各種變量的用法.
a.取代define
#define D_INT 100
#define D_LONG 100.29
………
const int D_INT = 100;
const D_INT = 100; //如果定義的int類型,可省略int.
const long D_LONG = 100.29;
………
const int& a = 100;
const替代define雖然增加分配空間,可它卻保證了類型安全.
在C標(biāo)準(zhǔn)中,const定義的數(shù)據(jù)相當(dāng)于全局的,而C++中視聲明的位置而定.
b.修飾指針相關(guān)的變量
以三組簡單的定義示意:
Group1:
int a = 0;
const int* b = &a;------------ [1]
int const *b = &a;------------ [2]
const int* const b = &a;---- [4]
Group2:
const char *p = "const";--------------[1]
char const *p = "const";--------------[2]
char* const p = "const";--------------[3]
const char * const p = "const";----[4]
Group3:
int a=0;
const int &b = a;---------------[1]
int const &b = a;---------------[2]
int & const b = a;--------------[3] //--->修飾引用時(shí),const被忽略
const int & const b = a;-----[4]
總結(jié):
1.如果const位于星號左側(cè),則const用來修飾指針?biāo)赶虻淖兞?
即指針指向的為不可變的.
2.如果const位于星號右側(cè),const就是修飾指針本身,即指針本身是
不可變的.
因此,[1]和[2]的情況相同,指針?biāo)赶騼?nèi)容不可變(const放在變量
聲明符的位置無關(guān)),
這種情況下不允許對內(nèi)容進(jìn)行更改,如不能*a = 3 ;
3.[3]中指針本身是不可變的,而指針?biāo)赶虻膬?nèi)容是可變的,這種情況
下不能對指針本身
進(jìn)行更改操作,如a++是錯誤的
4.[4]中指針本身和指向的內(nèi)容均為常量.(引用特殊:引用在使用增加
遇義時(shí),增加它代表的變量.所以qualifiers on reference are ignoredv.
延伸點(diǎn):
注意示例:
1.const int& reference = 1000;
2.char* p = "const"
char*& q ;
2.const在函數(shù)環(huán)境下的各種應(yīng)用
常用法示例如下:
const A& _Fun(const A& _in); //修飾引用型傳入?yún)?shù)
// A _Fun(const A& _in);
//A& _Fun(const A& _in);
//上面的兩種,在函數(shù)內(nèi)部有特殊的步驟,這里不詳提了…..
const A* _Fun( const A* _in); //修飾指針型傳入?yún)?shù)
void _Fun( ) const; //修飾class成員函數(shù)
const A& _Fun(A& _in ); //修飾返回值
const A & operator(const A& _in); //同時(shí)修飾傳入?yún)?shù)和返回值
a.修飾參數(shù)
如void _Fun(const A* _in)或 void _Fun(const A& _in);
它們被修飾后,在函數(shù)執(zhí)行期間行為特性同于上面的講解,
注意:這不會改變原來數(shù)據(jù)的是否是const的屬性.
b.修飾函數(shù)返回值
const A& _Fun( )
const A* _Fun( );
注意:由于生命期不同步的問題,不可將局部的變量的指針或引用返回(static除外).
另外,傳出來的視情況,代表不同的意思…
對于A&返回類型,你若將之賦與其它變量,那么它實(shí)際執(zhí)行的是將返回的變量
(或引用)代表的數(shù)據(jù)賦出..而你若將其它值賦予之,那么被賦予的是變量或引
用代表的數(shù)據(jù). 而const A& 一般是防止之做為左值被賦值.
這個地方還有很多的細(xì)節(jié)問題(譬如在連續(xù)賦值、返回的臨時(shí)對象的處理、
重載的const和非cosnt運(yùn)算符等等),讀者自己在實(shí)踐中需要多多總結(jié).
使用可變(mutable)成員隱藏實(shí)現(xiàn)細(xì)節(jié)
作者: Builder.com
鍵字 mutable 是一個奇怪的修飾符(specifier),它只能夠用于一個類的非靜態(tài)數(shù)據(jù)成員。下面我將討論 mutable 的語義和用法,但是首先我要解釋一下 C++ 對象模型的一個關(guān)鍵概念。
對象的狀態(tài)
一個對象的狀態(tài)由其非靜態(tài)數(shù)據(jù)成員的值構(gòu)成,因此,修改一個數(shù)據(jù)成員將會改變整個對象的狀態(tài)。將一個成員函數(shù)聲明為 const 能夠保證它不會改變對象的狀態(tài)。
然而在一些情況下,對象的邏輯狀態(tài)與基物理狀態(tài)之間可能有差別。例如,對于一個表示繪畫圖像的對象就存在這種情況。如果圖像還沒有更改,那
么我們就認(rèn)為其狀態(tài)沒有發(fā)生變化。然而,從底層實(shí)現(xiàn)方面來說,如果大對象在一段時(shí)間沒有活動,那么它們的內(nèi)存通常會被交換到一個文件中。交換一個圖像并不
會真地影響其狀態(tài),但是對象的一些數(shù)據(jù)成員可能會發(fā)生變化,在這里可能會發(fā)生變化的是指針、標(biāo)志位等。
在用戶調(diào)用一個諸如 Redraw() 之類的 const
成員函數(shù)時(shí),他們并不關(guān)心這個函數(shù)在內(nèi)部是如何實(shí)現(xiàn)的。從他們的角度來說,這個函數(shù)并不改變對象的邏輯狀態(tài),因此被聲明為 const。Redraw()
有可能修改對象的物理狀態(tài)這一事實(shí)是一個他們不應(yīng)該關(guān)心的實(shí)現(xiàn)細(xì)節(jié)。例如:
int Image::Redraw() const
{
if (isLoaded==false)
{
//..read image data from a disk into a local buffer
isLoaded=true; //changing a data member's value
}
//..paint image in the screen
}
class Image
可變(mutable)數(shù)據(jù)成員
如果嘗試編譯這段代碼,你會得到一個編譯錯誤。雖然 Redraw() 聲明為 const,但是它修改了一個數(shù)據(jù)成員。解決這個編譯錯誤的方法是將 isLoaded 聲明為一個 mutable 數(shù)據(jù)成員:
class Image
{
public:
int Redraw() const;
//..
private:
mutable bool isLoaded;//can be changed by a const function
};
不像普通的數(shù)據(jù)成員,const 成員函數(shù)可以修改 mutable 數(shù)據(jù)成員。
Mutable 數(shù)據(jù)成員的使用看上去像是騙術(shù),因?yàn)樗軌蚴?const 函數(shù)修改對象的數(shù)據(jù)成員。然而,明智地使用 mutable 關(guān)鍵字可以提高代碼質(zhì)量,因?yàn)樗軌蜃屇阆蛴脩綦[藏實(shí)現(xiàn)細(xì)節(jié),而無須使用不確定的東西,比如 const_cast<>。
volatile關(guān)鍵字
volatile是c/c++中一個鮮為人知的關(guān)鍵字,該關(guān)鍵字告訴編譯器不要持有變量的臨時(shí)拷貝,它可以適用于基礎(chǔ)類型
如:int,char,long......也適用于C的結(jié)構(gòu)和C++的類。當(dāng)對結(jié)構(gòu)或者類對象使用volatile修飾的時(shí)候,結(jié)構(gòu)或者
類的所有成員都會被視為volatile.
使用volatile并不會否定對CRITICAL_SECTION,Mutex,Event等同步對象的需要
例如:
int i;
i = i + 3;
無論如何,總是會有一小段時(shí)間,i會被放在一個寄存器中,因?yàn)樗阈g(shù)運(yùn)算只能在寄存器中進(jìn)行。一般來說,volatitle
關(guān)鍵字適用于行與行之間,而不是放在行內(nèi)。
我們先來實(shí)現(xiàn)一個簡單的函數(shù),來觀察一下由編譯器產(chǎn)生出來的匯編代碼中的不足之處,并觀察volatile關(guān)鍵字如何修正
這個不足之處。在這個函數(shù)體內(nèi)存在一個busy loop(所謂busy loop也叫做busy waits,是一種高度浪費(fèi)CPU時(shí)間的循環(huán)方法)
void getKey(char* pch)
{
while (*pch == 0)
;
}
當(dāng)你在VC開發(fā)環(huán)境中將最優(yōu)化選項(xiàng)都關(guān)閉之后,編譯這個程序,將獲得以下結(jié)果(匯編代碼)
; while (*pch == 0)
$L27
; Load the address stored in pch
mov eax, DWORD PTR _pch$[ebp]
; Load the character into the EAX register
movsx eax, BYTE PTR [eax]
; Compare the value to zero
test eax, eax
; If not zero, exit loop
jne $L28
;
jmp $L27
$L28
;}
這段沒有優(yōu)化的代碼不斷的載入適當(dāng)?shù)牡刂?,載入地址中的內(nèi)容,測試結(jié)果。效率相當(dāng)?shù)牡?,但是結(jié)果非常準(zhǔn)確
現(xiàn)在我們再來看看將編譯器的所有最優(yōu)化選項(xiàng)開關(guān)都打開以后,重新編譯程序,生成的匯編代碼,和上面的代碼
比較一下有什么不同
;{
; Load the address stored in pch
mov eax, DWORD PTR _pch$[esp-4]
; Load the character into the AL register
movsx al, BYTE PTR [eax]
; while (*pch == 0)
; Compare the value in the AL register to zero
test al, al
; If still zero, try again
je SHORT $L84
;
;}