在 C++ 中,const 不只是一個修飾符,更是一種設計契約。善用 const 能讓編譯器在編譯期攔截邏輯錯誤,避免執行期的驚喜。本文整理自《Effective C++》條款 3,探討指標、迭代器、函數回傳值與成員函數中的 const 應用。
記住口訣:「const 出現在 * 左邊修飾資料,右邊修飾指標本身」。
char greeting[] = "Hello"; char *p = greeting; // 非 const 指標,非 const 資料 const char *p = greeting; // 非 const 指標,const 資料(底層 const) char *const p = greeting; // const 指標,非 const 資料(頂層 const) const char *const p = greeting; // const 指標,const 資料
若要讓指標指向的資料不可修改,將 const 放在型別名稱之後(或 * 之前);若要讓指標本身不可改變指向,將 const 放在 * 之後。
STL 迭代器設計模擬指標行為。宣告迭代器為 const 如同宣告 T* const:迭代器不可改變指向,但所指物件可修改;若需所指物件也不可修改,請使用 const_iterator。
std::vectorvec; const std::vector ::iterator iter = vec.begin(); // iter 如 T* const *iter = 10; // OK:修改所指資料 ++iter; // Error:iter 本身為 const std::vector ::const_iterator cIter = vec.begin(); // cIter 如 const T* *cIter = 10; // Error:所指資料為 const ++cIter; // OK:迭代器本身可移動
const Rational operator*(...)),避免 (a*b)=c 這類無意義賦值。將成員函數宣告為 const,表示該函數不會修改物件的邏輯狀態(bitwise constness),且只有 const 成員函數可被 const 物件呼叫。這是介面設計的基石:
class Rational {
public:
// 只有 const 物件能呼叫 const 版本,非 const 物件可呼叫非 const 版本
int numerator() const; // 讀取操作,標記 const
void setNumerator(int); // 寫入操作,不可標記 const
};
良好的 const 正確性能讓類別介面更清晰,也能讓編譯器協助偵測「在不該修改的地方修改了資料」的錯誤。
const,除非確定需要修改。