關(guān)鍵字const多才多藝,
你可以用它在class外部修飾global或namespace作用域中的常量达舒,
或修飾文件,函數(shù)穗椅,或區(qū)塊作用域(block scope)中被聲明為static的對(duì)象下梢。
你也可以用它修飾class內(nèi)部的static和non-static成員變量客蹋。
面對(duì)指針,你也可以指出指針自身孽江,指針?biāo)肝镅扰鳎蛘邇烧撸ɑ蚨疾唬┦莄onst。
1. 指針
STL迭代器系以指針為根據(jù)塑模出來岗屏,所以迭代器的作用就像個(gè)T*
指針辆琅,
聲明迭代器為const就像聲明指針為const一樣,即聲明一個(gè)T* const
指針这刷,
表示這個(gè)迭代器不得指向不同的東西婉烟,但它所指的東西的值是可以改動(dòng)的。
如果你希望迭代器所指的東西不可以改動(dòng)暇屋,即希望STL模擬一個(gè)const T*
指針似袁,
你需要的是const_iterator
。
std::vector<int> vec;
...
// iter的作用像個(gè) T* const
const std::vector<int>::iterator iter = vec.begin();
// 沒問題咐刨,改變iter所指物
*iter = 10;
// 錯(cuò)誤昙衅,iter是const
++iter;
// cIter的作用像個(gè) const T*
std::vector<int>::const_iterator cIter = vec.begin();
// 錯(cuò)誤,*cIter是const
*cIter = 10;
// 沒問題所宰,改變cIter
++cIter;
2. 函數(shù)
const最具威力的用法是面對(duì)函數(shù)聲明時(shí)的應(yīng)用绒尊。
在一個(gè)函數(shù)聲明式內(nèi),const可以和函數(shù)返回值仔粥,各參數(shù)婴谱,函數(shù)自身(如果是成員函數(shù))產(chǎn)生關(guān)聯(lián)。
2.1 返回值
令函數(shù)返回一個(gè)常量值躯泰,往往可以降低因客戶錯(cuò)誤而造成的意外谭羔,而又不至于放棄安全性和高效性。
例如麦向,有理數(shù)的operator*
聲明式:
class Rational { ... };
const Rational operator* (const Rational& lhs, const Rational& rhs);
為什么返回一個(gè)const對(duì)象呢瘟裸?原因是如果不這樣客戶就能實(shí)現(xiàn)這樣的暴行:
Rational a,b,c;
...
// 在 a * b 的成果上調(diào)用 operator=
(a * b) = c;
將operator*
的回傳值聲明為const,可以預(yù)防那個(gè)“沒意思的賦值動(dòng)作”诵竭,這就是該這么做的原因话告。
2.2 參數(shù)
至于const參數(shù),沒有什么特別新穎的觀念卵慰,
他們不過就像local const對(duì)象一樣沙郭,你應(yīng)該在必要使用它們的時(shí)候使用它們。
除非你有需要改動(dòng)參數(shù)或local對(duì)象裳朋,否則請(qǐng)將它們聲明為const病线。
只不過多打6個(gè)字符,卻可以省下惱人的錯(cuò)誤。
2.3 成員函數(shù)
將const實(shí)施于成員函數(shù)的目的送挑,是為了確認(rèn)該成員函數(shù)可作用于const對(duì)象身上绑莺。
這一類成員函數(shù)之所以重要,基于兩個(gè)理由惕耕。
第一纺裁,它們似class接口比較容易被理解,這是因?yàn)樯耐唬弥膫€(gè)函數(shù)可以改動(dòng)對(duì)象內(nèi)容而哪個(gè)函數(shù)不行对扶,很是重要区赵。
第二惭缰,它們使“操作const對(duì)象”成為可能,這對(duì)編寫高效代碼是個(gè)關(guān)鍵笼才。
(1)成員函數(shù)重載
許多人漠視一件事實(shí):
兩個(gè)成員函數(shù)如果只是常量性不同漱受,可以被重載。
這實(shí)在是一個(gè)重要的C++特性骡送。
考慮以下class昂羡,用來表現(xiàn)一大塊文字:
class TextBlock{
public:
...
// operator[] for const對(duì)象
const char& operator[] (std::size_t position) const{
return text[position];
}
// operator[] for non-const對(duì)象
char& operator[] (std::size_t position){
return text[position];
}
private:
std::string text;
};
TextBlock
的operator[]
可被這樣使用:
TextBlock tb("Hello");
// 調(diào)用non-const TextBlock::operator[]
std::cout << tb[0];
const TextBlock ctb("World");
// 調(diào)用const TextBlock::operator[]
std::cout << ctb[0];
只要重載operator[]
并對(duì)不同的版本給予不同的返回類型,就可以令const和non-const TextBlock
獲得不同的處理:
// 沒問題摔踱,讀一個(gè)non-const TextBlock
std::cout << tb[0];
// 沒問題虐先,寫一個(gè)non-const TextBlock
tb[0] = 'x';
// 沒問題,讀一個(gè)const TextBlock
std::cout << ctb[0];
// 錯(cuò)誤派敷,寫一個(gè)const TextBlock
ctb[0] = 'x';
注意蛹批,上述錯(cuò)誤只因operator[]
的返回類型所致,
錯(cuò)誤起因于企圖對(duì)一個(gè)“由const版之operator[]
返回”的const char&
施行賦值動(dòng)作篮愉。
也請(qǐng)注意腐芍,non-const operator[]
的返回類型是一個(gè)reference to char,不是char试躏,
如果operator[]
只是返回一個(gè)char猪勇,下面這樣的句子就無法通過編譯。
tb[0] = 'x';
那是因?yàn)榈咴蹋绻瘮?shù)的返回類型是個(gè)內(nèi)置類型泣刹,那么改動(dòng)函數(shù)返回值從來就不合法。
縱使合法犀被,C++以by value返回對(duì)象這一事實(shí)椅您,意味著被改動(dòng)的其實(shí)是tb.text[0]
的一個(gè)副本,不是tb.text[0]
自身弱判。
(2)bitwise constness & logical constness
成員函數(shù)如果是const意味什么襟沮?
這有兩個(gè)流行概念:bitwise constness(又稱physical constness),和logical constness。
bitwise const陣營(yíng)的人相信开伏,成員函數(shù)只有在不更改對(duì)象之任何成員變量時(shí)才可以說是const膀跌,
也就是說它不更改對(duì)象內(nèi)的任何一個(gè)bit。
這種論點(diǎn)的好處是很容易偵測(cè)違反點(diǎn)固灵,編譯器只需尋找成員變量的賦值動(dòng)作即可捅伤。
bitwise constness正是C++對(duì)常量性的定義,因此巫玻,const成員函數(shù)不可以更改任何non-static成員變量丛忆。
不幸的是,許多成員函數(shù)雖然不十足具備const性質(zhì)卻能通過bitwise測(cè)試仍秤,
更具體的說熄诡,一個(gè)更改了“指針?biāo)肝铩钡某蓡T函數(shù)雖然不能算是const,但如果只有指針(而非其所指物)隸屬于對(duì)象诗力,
那么稱此函數(shù)為bitwise const不會(huì)引發(fā)編譯器異議凰浮,這導(dǎo)致反直觀結(jié)果。
這種情況導(dǎo)出所謂的logical constness苇本,
這一派擁護(hù)者主張袜茧,一個(gè)const成員函數(shù)可以修改它所處理的對(duì)象內(nèi)的某些bit,但只有在客戶端偵測(cè)不出情況下才得如此瓣窄。
例如笛厦,CTextBlock
class有可能高速緩存(cache)文本區(qū)塊的長(zhǎng)度以便應(yīng)付詢問:
class CTextBlock{
public:
...
std::size_t length() const;
private:
char* pText;
std::size_t textLength;
bool lengthIsValid;
};
std::size_t CTextBlock::length() const{
if(!lengthIsValid){
// 錯(cuò)誤,在const成員函數(shù)內(nèi)俺夕,不能賦值給textLength和lengthIsValid
textLength = std::strlen(pText);
lengthIsValid = true;
}
return textLength;
}
length
的實(shí)現(xiàn)裳凸,當(dāng)然不是bitwise const,因?yàn)?code>textLength和lengthIsValid
都可能被修改啥么。
這兩筆數(shù)據(jù)被修改對(duì)const CTextBlock
對(duì)象而言雖然可接受登舞,但編譯器不同意。
它們堅(jiān)持bitwise constness悬荣,怎么辦菠秒?
解決辦法很簡(jiǎn)單,利用C++的一個(gè)與const相關(guān)的擺動(dòng)場(chǎng)氯迂,mutable
(可變的)践叠。
mutable
釋放掉non-static成員變量的bitwise constness約束。
class CTextBlock{
public:
...
private:
// 這些成員變量可能總是會(huì)被改變嚼蚀,即使在const成員函數(shù)內(nèi)
mutable std::size_t textLength;
mutable bool lengthIsValid;
};