常量成員函數 (const member function), 可讀取類中的數據成員,但不能修改。
1 聲明
1.1 const 關鍵字
參數列表后,加 const 關鍵字,聲明為常量成員函數,表明其不被允許修改類的數據成員
下面的類,以年、月、日的形式來表示日期 (注意:年月日的聲明順序)
class Date { public: int GetYear() const { return y_; }int GetMonth() const { return m_; } int GetDay() const { return d_; } void AddYear(int n); // add n years private: int y_, m_, d_; };
1) 如果常量成員函數,企圖修改類的數據成員,則編譯器會報錯
// error : attempt to change member value in const function int Date::GetYear() const { return ++y_; }
2) 如果在類外面,“定義” 常量成員函數 ( “定義” = 實現,即 implementation),則 const 關鍵字不可省略
// error : const missing in member function type int Date::GetYear() { return y_; }
1.2 C++ 陷阱
類中成員變量的聲明順序,決定了成員變量的初始化順序。假設 Date 類中的構造函數為:
public: Date() : y_(2016), m_(9), d_(22) {}
此時,類中的成員函數,在類中的聲明順序 = 構造函數初始化列表順序,故 y_, m_, d_ 都能被順利的初始化為對應的值。
而當成員變量,在類中的聲明順序 ≠ 構造函數初始化列表順序 時,
public: Date() : y_(2016), d_(22), m_(d_-13) {}
根據成員變量的聲明順序,y_ 首先被初始化為 2016,然后再初始化 m_,但由於 d_ 並未被初始化,所以 m_ 的值是隨機的,最后初始化 d_ 為 22
這是因為,類的成員變量在初始化時,其初始化的順序只與聲明順序有關,而與在初始化列表中的順序無關。
2 調用
一個常量成員函數,可以被 const 和 non-const 類對象調用; 而非常量成員函數,例如 AddYear(),則只能被 non-const 型類對象調用。
void Date::AddYear(int n) { y_ += n; }
調用函數如下:
void f(Date& d, const Date& cd) { int i = d.GetYear(); // OK d.AddYear(1); // OK
int j = cd.GetYear(); // OK cd.AddYear(1); // error }
此時,const 修飾函數形參,是 “接口” 的常用指定形式, 這樣 數據 可以傳遞給 函數 而 本身不被修改。
C++ 中的類型轉換 const_cast,可以移除對象的 const 屬性,具體使用為: const_cast<T>(expression)
則上例中,要使 const 型類對象,調用類的 non-const 成員函數,可修改代碼如下:
void f(Date& d, const Date& cd) { int j = cd.GetYear(); // OK const_cast<Date&>(cd).AddYear(1); }
這種做法雖然是可以的,但它破壞了使用 const 來指定 “接口“ 的本意,並不推薦。
3 解釋
this 指針 默認是指向 non-const 型類對象的 const 型,因此,不能將 this 指針和 const 型類對象綁定,即 const 類對象無法調用類的成員函數
// 默認的 this 指針,指向 non-const 類對象 Date * const this;
在成員函數聲明的參數列表后加 const 后綴,表明其 this 指針指向 const 型類對象,如此, const 型類對象便可以調用常量成員函數了
// 常量成員函數中的 this 指針,指向 const 類對象 const Date * const this;
小結:
1) 類成員函數聲明中的 const 后綴,表明其 this 指針指向 const 型類對象,因此該 const 類對象,可以調用常量成員函數 (const member function)
2) 一個成員函數,如果對數據成員只涉及讀操作,而不進行修改操作,則盡可能聲明為常量成員函數
參考資料:
<C++ Programming Language_4th> ch 16.2.9.1
<C++ Primer_5th> ch 7.1.2
<Effective C++_3rd> Item 3, item 27
<More Effective C++> Item 2
<劍指 offer> 第 7 章