const は constant の略であり、本来は「変わらない」「容易に変えられない」という意味です。C++ では、組み込み型の変数、カスタムオブジェクト、メンバ関数、戻り値、関数パラメータを修飾するために使われます。

C++ の const は、意味的制約を指定することを可能にし、コンパイラはその制約を強制します。プログラマはコンパイラに対して、特定の値が一定に保たれることを伝えられます。プログラミング中に実際に変更されない値があるなら、明確に const を使うべきです。そうすればコンパイラの助けを得られます。

一、constが普通の型の変数を修飾する

const int  a = 7; 
int  b = a; // 正确
a = 8;       // 错误,不能改变

a は定数として定義され、a を b に代入することはできますが、a に再度代入することはできません。定数に代入することは違法です。a はコンパイラによって定数と見なされ、その値の変更は許可されないからです。

次に以下の操作を見てみましょう:

例

#include<iostream> using namespace std; int main(void) { const int a = 7; int *p = (int*)&a; *p = 8; cout<<a; system("pause"); return 0; }

const 変数 a について、変数のアドレスを取得して int へのポインタにキャストして代入し、*p = 8; を用いて変数 a のアドレス内の値に再代入し、a の値を出力して確認します。

下のデバッグウィンドウから、a の値は 8 に変更されていますが、出力結果は依然として 7 です。

結果からわかるように、コンパイラは a の値が最初に定義された 7 であると見なすため、const a に対する操作は上記のような状況を引き起こします。ですから、const 変数にむやみに代入しようとしないでください。予期しない動作を引き起こす可能性があります。

コンパイラに上記のような const への操作を気づかせたくない場合は、const の前に volatile キーワードを付けることができます。

volatile キーワードは const とは逆に、変わりやすく、容易に変更されるという意味です。そのため、コンパイラによって最適化されず、コンパイラも a 変数に対する操作を変更しません。

例

#include<iostream> using namespace std; int main(void) { volatile const int a = 7; int *p = (int*)&a; *p = 8; cout<<a; system("pause"); return 0; }

出力結果は期待どおり 8 です。

二、constはポインタ変数を修飾する。

const がポインタ変数を修飾する場合、次の3つのケースがあります。

  • A: const がポインタの指す内容を修飾する場合、内容は変更不可の変数となります。
  • B: const がポインタを修飾する場合、ポインタは変更不可の変数となります。
  • C: const がポインタとポインタの指す内容の両方を修飾する場合、ポインタとポインタの指す内容はどちらも変更不可の変数となります。

A については:

const int *p = 8;

ポインタの指す内容 8 は変更できません。const が * の左側にあるため、「左定値」と略されます。

B については:

int a = 8;
int* const p = &a;
*p = 9; // 正确
int  b = 7;
p = &b; // 错误

const ポインタ p は、その指すメモリアドレスを変更できませんが、その内容は変更できます。「右定向」と略されます。const が * の右側にあるためです。

C については:A と B の組み合わせです。

int a = 8;
const int * const  p = &a;

この場合、const p の指す内容と指すメモリアドレスはどちらも固定されており、変更できません。

A、B、C の3つのケースについて、const が * のどの位置にあるかに応じて、覚えやすいように3つのフレーズにまとめました:「左定値、右定向、constは不変量を修飾する」。

三、constパラメータの受け渡しと関数の戻り値。

const が関数パラメータを修飾する場合、3つのケースに分けられます。

A:値渡しの場合のconst修飾。通常この場合、const 修飾は不要です。関数が自動的に一時変数を生成して実引数の値をコピーするからです。

例

#include<iostream> using namespace std; void Cpf(const int a) { cout<<a; // ++a; はエラーです。a は変更できません } int main(void) { Cpf(8); system("pause"); return 0; }

B:const パラメータがポインタの場合、ポインタが意図せず改ざんされるのを防ぐことができます。

例

#include<iostream> using namespace std; void Cpf(int *const a) { cout<<*a<<" "; *a = 9; } int main(void) { int a = 8; Cpf(&a); cout<<a; // a は 9 system("pause"); return 0; }

C:カスタム型のパラメータ受け渡しでは、パラメータをコピーするための一時オブジェクトが必要です。一時オブジェクトの構築にはコンストラクタの呼び出しが必要で、時間がかかるため、const と参照渡しを組み合わせた方法を採用します。

また、一般的な int、double などの組み込み型については、参照渡しを採用しません。

例

#include<iostream> using namespace std; class Test { public: Test(){} Test(int _m):_cm(_m){} int get_cm()const { return _cm; } private: int _cm; }; void Cmf(const Test& _tt) { cout<<_tt.get_cm(); } int main(void) { Test t(8); Cmf(t); system("pause"); return 0; }

出力結果8。

const が関数の戻り値を修飾する場合について。

const が戻り値を修飾する場合、3つのケースがあります。

A:const が組み込み型の戻り値を修飾する場合、修飾しても修飾しなくても戻り値の働きは同じです。

例

#include<iostream> using namespace std; const int Cmf() { return 1; } int Cpf() { return 0; } int main(void) { int _m = Cmf(); int _n = Cpf(); cout<<_m<<" "<<_n; system("pause"); return 0; }

B: const がカスタム型の戻り値を修飾する場合、このとき返された値は左辺値として使用できません。代入も変更もできません。

C: const が返されるポインタまたは参照を修飾する場合、const を指すポインタを返すかどうかは、ユーザーに何をさせたいかによって決まります。

四、constがクラスメンバ関数を修飾する

const がクラスメンバ関数を修飾する目的は、メンバ関数が呼び出し元オブジェクトの値を変更するのを防ぐことです。呼び出し元オブジェクトの値を変更したくない場合は、すべてのメンバ関数を const メンバ関数として宣言する必要があります。

注意:const キーワードは static キーワードと同時に使用できません。static キーワードは静的メンバ関数を修飾し、静的メンバ関数は this ポインタを持たないためインスタンス化できません。const メンバ関数は特定のインスタンスに固有でなければなりません。

以下の get_cm()const; 関数は const メンバ関数を使用しています:

例

#include<iostream> using namespace std; class Test { public: Test(){} Test(int _m):_cm(_m){} int get_cm()const { return _cm; } private: int _cm; }; void Cmf(const Test& _tt) { cout<<_tt.get_cm(); } int main(void) { Test t(8); Cmf(t); system("pause"); return 0; }

get_cm() から const 修飾を取り除くと、Cmf に渡された const _tt がオブジェクトの値を変更していなくても、コンパイラは関数がオブジェクトの値を変更すると見なします。そのため、要件に従って、オブジェクトの内容を変更する必要のないすべての関数を const メンバ関数にするよう心がけましょう。

メンバ関数がオブジェクト内の特定のメンバを変更したい場合はどうすればよいでしょうか? その場合は、mutable キーワードを使用してそのメンバを修飾できます。mutable の意味も「変わりやすい」「容易に変わる」という意味です。mutable キーワードで修飾されたメンバは常に変化し続けることができます。次の例を参照してください。

例

#include<iostream> using namespace std; class Test { public: Test(int _m,int _t):_cm(_m),_ct(_t){} void Kf()const { ++_cm; // エラー ++_ct; // 正しい } private: int _cm; mutable int _ct; }; int main(void) { Test t(8,7); return 0; }

ここでは、Kf()const 内で ++_ct; を使って _ct の値を変更していますが、++_cm で _cm を変更するとエラーになります。++_cm が mutable で修飾されていないためです。

原文アドレス:https://www.cnblogs.com/Forever-Kenlen-Ja/p/3776991.html