const 是 C++ 中的關(guān)鍵字,它會在編譯期間(時機很重要),告訴編譯器這個對象是不能被修改的。初學(xué)者一般會認為 const 是個麻煩的東西,因為它常常讓你的程序編譯不通過,而去掉了 const 之后,就不會有這么多「問題」了,實不相瞞,其實我剛學(xué) C++ 的時候,有一段時間就處于這種狀態(tài)。
但實際上,const 非常有用:
- 他可以保護變量,防止它們被無意的修改。
- 它可以傳達有效的信息給讀你代碼的人:這個變量是不能被修改的,或者這個函數(shù)不會修該對象的成員,等。
-
const關(guān)鍵字可以優(yōu)化編譯結(jié)果,讓可執(zhí)行程序更為緊湊。 -
const修飾的變量還可以寫入 ROM 介質(zhì)。
總之,我認為合理的使用 const 可以讓你寫出來的程序更為嚴謹、健壯,不易出錯,乃至于更加高效。
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 常量的強制轉(zhuǎn)換操作,即將 const 變量強制轉(zhuǎn)換為非 const 的,或相反,尤其是前者,因為這會破壞你對常量賦予的承諾,可能會導(dǎo)致誤操作,從而引入隱蔽 bug。
const int i = 9;
// 強制將常量 i 轉(zhuǎn)換為普通變量
const_cast<int&>(i) = 6;
int j;
// 編譯失敗; 因為該語句強制將變量 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ù)等價,因為它們都是值傳遞(pass-by-copy),進入函數(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ù)會修改實參 strname 了。除此之外, setName 還可以被重載(overload)
void setName(string& name); // 這里也只能是引用傳遞
void setName(const string& name);
調(diào)用規(guī)則為,當(dāng)你的實參為 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)部成員進行修改。
最后,我們來看一下 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.
參考: