constキーワードを使用すると、指定された変数は、一度初期化されたら変更されるべきではない、ということをコンパイラに伝えることができます。それ以外にも、宣言したクラスの関数は、クラスのデータを変更しないということを宣言するのにも使用されます。
予想外に上書きされるのから保護するのが適切な場所では、常にconstを使うのが良いプラクティスであると言われています。できあがったプログラムに後からconstを追加しようとすると、滝のように次々とconstを付けていく必要があります。コードの開発サイクルの初期からconstを実装していくのがベストです。これにより、適切な宣言とconstの使用が行えるようになります。
constキーワードは複数の意味を持っていて様々な場所に使用することができます。無意味な場所にさえ付けることが可能です。
constにより何が保護されるのかを理解するために、右の説明を読んでから左側の宣言を見てください。
const char * str; // 「変更できない文字列」に対するポインタです。ただし、ポインタ自身を他のポインタにリダイレクトすることは可能です char const * str; // 上の例と同じです。 char * const str; // 文字列に対する「変更できないポインタ」です。ただし、文字列そのものを変更することは可能です
C++の参照もこれと似ています。ただし、参照に対してconstを適用すると、参照した後に他の要素にリダイレクトすることはできません。
const char & str; // 「変更できない文字列」に対する参照 char const & str; // 上記と同じ
ここまでの説明は比較的簡単です。しかし、より複雑なケースでは解読するのもよりむずかしくなっていきます。以下の例を見て下さい:
char * const * data; // 文字列への「不変なポインタ」へのポインタ char const ** data; // 「不変な文字列」へのポインタへのポインタ const char ** data; // 「不変な文字列」へのポインタへのポインタ char ** const data; // 文字列へのポインタへの「不変なポインタ」 char * const * const data; // 文字列への「不変なポインタ」への「不変なポインタ」
constは型の後に置かなければならないと信じている人も中にはいますが、通常のポインタ以外の型(“char”など)であれば、型の前後のどちらであっても置くことは自由です。 今まで書いてきたコード、もしくは組織のコーディング標準にあわせたフォーマットで記述してください。それ以外の場合でも一貫性を保つようにしてください。 もしもポインタに対してconstを適用したい場合には、アスタリスクの後にconstを配置するようにしましょう。
オブジェクトの属性が、newで初期化されてからは、deleteされるまでの期間の間に変更されることがないのであれば、そのフィールドに対してconst宣言を行うのは良い習慣です。
もっとも一般的なconstの使用方法としては、ポインタで指定されているオブジェクトや参照されているオブジェクトのデータの保護になります:
void func ( const MyObject * data ); // MyObjectはfuncの中では変更できません void func ( const MyObject & data ); // MyObjectはfuncの中では変更できません
constの置く位置を、型の前にしても、後ろに置いても違いはありません。次の例は前の例と同じ意味になります:
void func ( MyObject const * data ); // ( const MyObject * )と同じ void func ( MyObject const & data ); // ( const MyOjbect & )と同じ
しかし、constをポインタや参照の後ろに移動すると、”const”で保護される対象が変わります。ポインタの後のconstは、ポインタに対してのみ有効になり、ポインタが指しているデータは保護されません。
void func ( MyObject * const data ); // コピーされたMyObjectポインタへの保護(不要な保護)
関数の中では、MyObjectへのポインタは操作できませんが、MyObject自身の操作は自由にできてしまいます。しかし、ポインタの値自体は関数呼び出し時に値渡しでもらってきたもので、実際は関数ローカルな変数なので、なんの効果も現しません。関数呼び出しを行う外の関数にとっては、関数の中のローカルのポインタが変更されたかどうかに関しては何も影響がないからです。
参照の後ろにconstを付けるのは必要はなく、むしろ避けるべきです。参照は暗黙的にconst宣言されているのと同じなので、元々他の参照先に差し替えることはできないからです。
void func ( MyObject & const data ); // MyObjectへの参照に対する、不要な保護
ここでは、constはもともと操作できない参照を保護しているだけです。
他の便利使い方としては、returnでprivateなデータを返す場合があります。privateなデータを外部に見せるが、クラスの外では操作されたくない、というケースで有効です。
const MyObject & MyClass::func ( MyObject & data ); // funcは、変更できないMyObjectを返します
オブジェクトの内部のデータを変更しないインスタンスメソッドというのもよく作成されます。これらのメソッドはアクセッサとして知られています。これはconstをつけて宣言をすべきです。これを付けると、このメソッド内のthisポインタに作用します。実際にこのポインタが”MyClass *”だったとしても、”const MyClass *”として解釈されます。このポインタを使用してオブジェクトを変更することはできなくなります。
void MyClass::func ( MyOjbect & data ) const; // この関数はクラスのデータを変更することはできません
もしもconstのオブジェクトがあったとしたら、そのオブジェクトの中のメソッドのうち、呼び出せるのはconstの付いたメソッドだけです。理由としては、通常のメソッドが呼べてしまうとすると、そのオブジェクトのポインタをthisポインタとして渡す必要があります。しかし、そのクラスがconstが付いた”const MyClass”オブジェクトだったとすると、”MyClass *”ではなくて、”const MyClass *”しか得ることができませんので、constメソッドしか呼べないのです。
void MyClass::const_func() const; void MyClass::func(); const MyClass object; object.const_func(); // ok object.func(); // constオブジェクトの非constクラスは呼ぶことができません
同様の理由で、constメソッドの中からそのオブジェクトのメソッドを呼ぶ場合には、他のconstメソッドしか呼ぶことはできません。
関連トピック: const_cast, mutable