C/C++におけるconstキーワードについて考える

12347 ワード

constキーワードは定数を定義するだけでなく、関数のパラメータ、戻り値、メンバー関数のthisポインタを修飾することもできます.constを適切に使用すると,プログラマーの不注意による変数の変更を効果的に回避でき,プログラムの頑丈性を向上させることができる.次に、constの異なるシーンでの役割について説明します.
一、定数の定義
const int x = 1;   //    x

constで定義される定数とマクロで定義されるdefineで定義される定数の違いは、const定数にタイプがあることである.マクロ定義の定数にはタイプがなく、単純な文字列置換のみが使用されます(この操作はプリプロセッサによって実行されます).const定数を使用する場合は、const以外の変数との値付け操作に注意してください.具体的には、次のようになります.
1.1単純な割り当て(参照またはポインタなし)
    const int x1 = 1;
    int y1 = x1;    //ok

    int x2 = 1;
    const int y2 = x2;   //ok

単純な付与(値伝達方式、参照またはポインタなし)操作では、実際には右操作数の値を左操作数アドレスにコピーするため、2つの操作数が指すデータは独立しており、互いに影響しない.
1.2定数=変数(参照またはポインタ)
    int x1 = 1;
    const int &y1 = x1;    //ok

    int value = 1;
    int *x2 = &value;
    const int *y2 = x2;    //ok

定数の参照(ポインタ)が変数を指すことを許可します.これにより、y 1(または*y 2)で対応するメモリ領域の値1を変更することはできませんが、x 1(または*x 2)で変更することはできます.
1.3変数=定数(参照またはポインタ)(重要な原則)
    const int x1 = 1;
    int &y1 = x1;    //error

    int value = 1;
    const int *x2 = &value;
    int *y2 = x2;    //error

コンパイラは、プログラマがこの変数で定数の値を変更する可能性があると考えているため、変数を許可しない参照(ポインタ)は定数を指します.これは許可されません.
tips:(以下の観点は著者自身が理解し、誤りがあれば指摘したい)変数と定数の違いは「言語階層」で言えば、プログラマーがプログラムを設計する際に、定数と変数を適切に設計することによって、相応の機能を完成する.定数または変数の区別なしに、変数または定数が指すメモリアドレスは、任意の変数で変更できます.例1.2のコード例のように、x 1とy 1は同じメモリアドレスを指し、y 1は定数であり、その値を変更することはできないが、x 1で変更することができ、実際にはy 1の値を変更することもできる.
    int x1 = 1;
    const int &y1 = x1;

    x1 = 2;   //ok

二、修飾関数パラメータ
関数伝達については、ブログ記事:C/C++の関数伝達方式を参照してください.ここでは省略します.
関数内に変更されたパラメータ操作がない場合は、パラメータをconstとして定義することが望ましい.
2.1非const参照パラメータ
関数パラメータがconst参照でないと定義されている場合、パラメータは実際には実パラメータの別名であり、実パラメータのコピーは生成されません.しかし、constオブジェクトでは初期化できないし、フォント値や右値を生成する式の実パラメータでは初期化できないため、この定義パラメータは使用時にあまり柔軟ではありません.(『C++Primer』P 205)したがって、「修正する必要のない参照パラメータをconst参照として定義すべきである」.(上記1.3で述べたように)
2.2 const参照パラメータ
関数パラメータがconst参照として定義されている場合、実パラメータのコピーは生成されず、関数はパラメータを変更できません.
void StringCopy(char *strDestination, const char *strSource);
void swap ( int * const p1 , int * const p2 );  //     swap     p1 p2   

三、修飾関数の戻り値
const修飾関数で値を返すのは一般的ではありませんが、ここではいくつかの状況を簡単に列挙します.
3.1「伝値」戻り値
戻り値がプライマリ・コール関数に「値伝達」で返される場合、const修飾を追加することは意味がありません.
関数の戻り値が「値伝達方式」を採用すると、関数は戻り値を外部の一時的なメモリセルにコピーするため、const修飾を加えても価値がありません.例えば関数int GetInt(void)をconst int GetInt(void)と書くのは意味がありません.
3.2「アドレス」の戻り値
戻り値がプライマリ・コール関数、すなわち戻り値がポインタである「アドレス」で返される場合、戻り値(ポインタが内容を指す)は変更されず、const修飾の定数ポインタにのみ付与されます.(定数ポインタとポインタ定数の違いは、文章の末尾に説明があります)
const char * GetString(void);
char *str = GetString();   //error
const char *str = GetString();   //ok

3.3「参照の転送」の戻り値
コールバック関数は、3.2のように、戻り値を「参照」でプライマリ・コール関数に返します.
tips:関数の戻り値にconstを使用するもう一つの目的は、関数呼び出し式を左値として使用できないことを制限することです.例は次のとおりです.
int & min ( int &i, int &j); 左の値を返すため、関数呼び出しを割り当てることができます:min(a,b)=4;しかし、関数の戻り値がconstに限定される場合、すなわち定義:const int&min(int&i,int&j);では,min(a,b)呼び出しに値を付けることはできない.
四、修飾メンバー関数(関数体/オブジェクト/*this)
C++では、constはメンバー関数、すなわちクラスのインスタンス化されたオブジェクトを修飾するthisポインタを修飾するためにも使用できます.
thisポインタについては、『C++Primer』P 377に以下のように記述されている.
通常の非constメンバー関数では、thisのタイプはクラスタイプオブジェクトを指すconstポインタです.thisが指す値は変更できますが、thisが保存するアドレスは変更できません.constメンバー関数では、thisのタイプはconstクラスタイプのオブジェクトを指すconstポインタです.thisが指すオブジェクトも、thisが保存するアドレスも変更できません.
上記の説明の意味は、const修飾メンバー関数は、実際にはType const*thisと理解され、すなわち、このメンバー関数のオブジェクトはconstオブジェクトとみなされ、データメンバーを変更することはできないということである.
ただしconstの位置は特殊で、宣言の末尾には、言語設計者が発見したと推定され、他の場所で占有されているのではないでしょうか~~const修飾のメンバー関数は、クラス/オブジェクトのデータメンバーを変更することはできません.したがって、データ・メンバーを変更しないメンバー関数はconstタイプとして宣言する必要があります.
コンパイラは、constメンバー関数を作成するときにデータ・メンバーを誤って変更したり、他の非constメンバー関数を呼び出したりすると、エラーを指摘し、プログラムの堅牢性を向上させるに違いありません.
class MyClass {
    int member ;
    public:
    int getCount ( ) const;
};

classname :: getCount( )
{   member =4 ;   //error,“      ”,        
    return member;
}

constメンバー関数については、a.constメンバー関数は、オブジェクトがconstの性質を持っているかどうかにかかわらず、オブジェクトのデータを変更することはできません.コンパイル時に、メンバーデータを変更するかどうかを根拠に、チェックします.b.constメンバー関数は、関数で他の非constメンバー関数を呼び出すことはできません.(constメンバー以外の関数はデータメンバーを変更する可能性があります)c.constオブジェクトはconstメンバー関数にのみアクセスできます.constオブジェクトではなくconstメンバー関数にもconstメンバー関数にもアクセスできます.(constメンバー関数ではなくconstオブジェクトのデータメンバーを変更することはできません)
五、constに関するその他の知識点
5.1 constのリンク属性
変数または関数のリンク・プロパティについては、「リンク・プロパティとストレージ・タイプ」に移動します.
constオブジェクトのデフォルトのリンクプロパティはinternal、すなわちファイルのローカル変数です.
a.グローバル役割ドメインでconst以外の変数を定義すると、プログラム全体にアクセスできます.
//《C++ Primer》 P50
//            
//file1.cc
int counter;  //define
//file2.cc
extern int counter;   //uses counter from file1
++counter;    //increments counter defined in file1

b.グローバル役割ドメインで宣言されるconst変数は、そのオブジェクトを定義するファイルのローカル変数である.この変数値はそのファイルに存在し、他のファイルにアクセスできません.const変数をextern(リンクプロパティを明示的に変更)に実行すると、プログラム全体でconstオブジェクトにアクセスできます.
//  (《C++ Primer》 P50  )
//const          ,            ,         
//file_1.cc
//defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();   // extern  const       

//file_2.cc
extern const int bufSize;  //uses bufSize from file_1
for(int index=0;index!=bufSize;++index);    //uses bufSize defined int file_1
    //...

5.2 const定義定数から非const変数への変換
まず、サンプルコードを見てみましょう.
    int i = 0;
    const int &j = i;  //ok,    ,int -> const int

    const int x = 1;
    int &y = x;     //error
    int &y = const_cast<int&>(x);    //ok,    ,const int -> int

上記のコードから、const参照変数は非const変数にバインドされ、逆にエラーが報告されることがわかります(非const参照変数はconst変数の値を変更する可能性があります).この場合、C++のconst_を借りる必要があります.castは強制タイプ変換を行います.const_castは、その名の通り、式のconst属性を変更します.const_castは変数のconstプロパティのみを削除(または追加、非常用)でき、他のタイプの強制変換を完了するために使用するとコンパイルエラーが発生します.
C++いくつかの強制タイプ変換別の記事が表示されます:C++の4つの強制タイプ変換演算子の関連と区別
tips: a. const_castは元の変数のconst属性を変更せず、C++タイプ変換の詳細を参照します–const_castの一節:
const_の使用castはconstプロパティを削除しますが、実際に元のクラスタイプ(または基本タイプ)のconstプロパティを変更するわけではありません.このインタフェースでタイプの値を変更できるインタフェース(ポインタまたは参照)を提供しているだけです.これもconst_caseはポインタや参照を変換する1つの理由しかないでしょう.
    int i = 0;
    const int &j = const_cast<const int&>(i);
    i = 1;    //ok

    const int x = 1;
    int &y = const_cast<int&>(x);
    x = 0;   //error

b. const_castでは、同じアドレスに2つの異なる値(const変数とconst_castについて)があるという「定数オーバーラップ」の問題が発生する可能性があります.使用するときは注意してください.
5.3定数ポインタとポインタ定数
字面の意味から見ると、「定数ポインタ」はポインタに重点を置いている:定数を指すポインタ;一方、「ポインタ定数」は、ポインタタイプの定数に重点を置きます.
5.3.1定数ポインタ
const int *p;
int const *p;

これはポインタで変更することはできませんが、元の宣言で変更することができます.このポインタが指すオブジェクト(オブジェクトアドレス、ポインタ変数の値)を変更できます.
int a=1;
const int* p=&a;
*p=3;   //error
a=3;  //ok

5.3.2ポインタ定数
int* const p;

このポインタでは、オブジェクトを指す値を変更できますが、ポインタが指すオブジェクトは変更できません.
int b=2;
int* const q=&b;
q=&a;  //error
*q=3;   //ok

tips:constは誰を修飾して、誰は変えることができません
参考資料:[C/C++]const詳細(修飾変数、入力パラメータ、戻り値、メンバー関数)修飾関数と関数戻り値のconstの違いconstパラメータ、const戻り値とconst関数const修飾子はconstの内部リンク属性をまとめる