const
是 C++
中的關(guān)鍵字蟋座,它會在編譯期間(時機(jī)很重要),告訴編譯器這個對象是不能被修改的假勿。初學(xué)者一般會認(rèn)為 const
是個麻煩的東西这吻,因?yàn)樗3W屇愕某绦蚓幾g不通過,而去掉了 const
之后般堆,就不會有這么多「問題」了在孝,實(shí)不相瞞,其實(shí)我剛學(xué) C++
的時候淮摔,有一段時間就處于這種狀態(tài)私沮。
但實(shí)際上,const
非常有用:
- 他可以保護(hù)變量和橙,防止它們被無意的修改仔燕。
- 它可以傳達(dá)有效的信息給讀你代碼的人:這個變量是不能被修改的,或者這個函數(shù)不會修該對象的成員魔招,等晰搀。
-
const
關(guān)鍵字可以優(yōu)化編譯結(jié)果,讓可執(zhí)行程序更為緊湊办斑。 -
const
修飾的變量還可以寫入 ROM 介質(zhì)外恕。
總之,我認(rèn)為合理的使用 const
可以讓你寫出來的程序更為嚴(yán)謹(jǐn)、健壯吁讨,不易出錯髓迎,乃至于更加高效。
const 與變量
用 const
修飾常量建丧,比較容易混淆的是:「 const
類型的常量指針」和「指針?biāo)赶虻膬?nèi)容為 const
」之間的區(qū)別:
int i = 0;
const int* p1 = &i;
int const* p2 = &i;
上面代碼表示指針?biāo)傅膬?nèi)容為 const
排龄,你不能做 *p1 = 1;
這樣的操作,但可以 p1++;
int* const p3 = &i;
const int* const p4 = &i;
以上代碼中翎朱,p3
為 const
指針橄维,你無法修改 p3
,如 p3++;
拴曲,但你可以執(zhí)行 *p3 = 1;
争舞。最后一行中,p4
和其所指向的內(nèi)容都是 const
的澈灼。這里的規(guī)律是:如果 const
在 *
號左邊竞川,就表示指針?biāo)傅膬?nèi)容是常量,否則指針本身是常量叁熔。
另外委乌,我們要盡可能的減少對 const
常量的強(qiáng)制轉(zhuǎn)換操作,即將 const
變量強(qiáng)制轉(zhuǎn)換為非 const
的荣回,或相反遭贸,尤其是前者,因?yàn)檫@會破壞你對常量賦予的承諾心软,可能會導(dǎo)致誤操作壕吹,從而引入隱蔽 bug
。
const int i = 9;
// 強(qiáng)制將常量 i 轉(zhuǎn)換為普通變量
const_cast<int&>(i) = 6;
int j;
// 編譯失敗; 因?yàn)樵撜Z句強(qiáng)制將變量 j 轉(zhuǎn)換成了常量
static_cast<const int&>(j) = 7;
const 與函數(shù)
const
與函數(shù)結(jié)合删铃,我們需要考慮3種情況:
-
const
常量作為函數(shù)的參數(shù)傳入 - 函數(shù)返回
const
類型 - 聲明類的成員函數(shù)為
const
當(dāng) const
常量作為參數(shù)傳入時耳贬,該常量一定需要是引用類型,否則 const
起不到應(yīng)有的作用泳姐,例如
class Dog {
string name_;
public:
Dog(const string& name) { name_ = name; }
void setName(const string name);
void setName(string name);
};
上面兩個 setName()
函數(shù)等價效拭,因?yàn)樗鼈兌际侵祩鬟f(pass-by-copy),進(jìn)入函數(shù)的參數(shù)是一個臨時副本胖秒,既然是一個副本缎患,修改它或不修改它對外部沒有任何影響,所以這樣的聲明沒有意義阎肝。要使上面的 const
產(chǎn)生意義挤渔,應(yīng)該改為傳引用(pass-by-reference)的方式
void setName(const string& name);
...
Dog dog("");
string strname = "xiao-D";
dog.setName(strname)
此時,外部調(diào)用 setName
就不用擔(dān)心該函數(shù)會修改實(shí)參 strname
了风题。除此之外判导, setName
還可以被重載(overload)
void setName(string& name); // 這里也只能是引用傳遞
void setName(const string& name);
調(diào)用規(guī)則為嫉父,當(dāng)你的實(shí)參為 const
類型時,會調(diào)用 void setName(const string& name);
函數(shù)眼刃,否則調(diào)用 void setName(string& name);
和函數(shù)的參數(shù)為 const
一樣绕辖,當(dāng)函數(shù)返回 const
類型時,也需要是引用類型擂红,否則沒有意義仪际,即
const string& getName() { return name_; }
這樣可以防止外部對 object 的內(nèi)部成員進(jìn)行修改。
最后昵骤,我們來看一下 const
類型的函數(shù)树碱,將以下函數(shù)加到 Dog
類中:
void bark() const { cout << "dog " << name_ << " bark."; }
const
類型的函數(shù)表明該函數(shù)不會修改對象的成員,同時該函數(shù)也不可以調(diào)用非 const
函數(shù)变秦,例如如下操作都是不允許的
void bark() const {
name_ = "da-D"; // can't modify any member
setName("mark"); // can't call a non-const function
}
除此之外成榜,const
類型的函數(shù)也是可以重載的,即
void bark() const { cout << "const dog " << name_ << " bark." << endl; }
void bark() { cout << "non-const dog " << name_ << " bark." << endl; }
調(diào)用規(guī)則是蹦玫,當(dāng)對象是 const
類型時赎婚,調(diào)用 const
類型的函數(shù),否則調(diào)用非 const
類型的函數(shù)钳垮。
Dog d1;
d1.bark("QQ");
const Dog d2;
d2.bark("Wechat");
得到的結(jié)果為:
non-const dog QQ bark.
const dog Wechat bark.
參考: