C++のconst修飾子について詳しく解説

C++において、constは重要なキーワードの一つです。

しかし、初心者にとってはその意味や使い方がわかりづらいこともあります。

本記事では、C++のconstについて詳しく解説し、サンプルコードを交えてわかりやすく解説します。

目次

constとは何か

C++におけるconstは、変数や関数の引数、戻り値などを定数として扱うためのキーワードです。つまり、一度値が代入されたらその後変更できないようにすることができます。

例えば、以下のようにint型の変数を宣言し、constキーワードを付けることで定数として扱うことができます。

const int num = 10;

この場合、numに対して再代入を行おうとするとコンパイルエラーが発生します。

また、関数の引数や戻り値にもconstキーワードを使用することができます。これによって、関数内部で引数や戻り値の値を変更することを禁止して、意図しない代入によるバグを予防することができます。

void func(const int arg) {
    // argに対して再代入しようとするとコンパイルエラーが発生する
}

const int func2() {
    return 10;
}

以上がconstキーワードの基本的な使い方です。次は具体的な利用方法について解説します。

constの使い方

C++におけるconstは、変数や関数、ポインタなどに対して使用することができます。ここでは、それぞれの使い方について詳しく解説します。

変数の宣言時にconstを使う

変数を宣言する際に、その変数が値を変更されないようにしたい場合は、constキーワードを使用します。以下は、int型の定数を宣言する例です。

const int num = 10;

このように定義された変数は、後から値を変更することができません。

関数の引数にconstを使う

関数の引数にconstキーワードを付けることで、その引数が関数内で値を変更されないようにすることができます。以下は、int型の引数を取り、その値を2倍した結果を返す関数です。

int double_num(const int num) {
    return num * 2;
}

このように定義された関数では、numの値が関数内で変更されることはありません。

関数の戻り値にconstを使う

関数の戻り値にconstキーワードを付けることで、その戻り値が呼び出し元で値を変更されないようにすることができます。以下は、int型の定数を返す関数です。

const int get_num() {
    return 10;
}

const int result = get_num();
result += 5; //コンパイルエラー

このように定義された関数では、get_num()から返される値は呼び出し元で変更することができません。

constメンバ関数

クラス内部で定義されたメンバ関数でも、引数や戻り値同様にconstキーワードを使用することができます。

constキーワードがついたメンバ関数は、「オブジェクト自身」もしくは「オブジェクト自身から参照・ポインタ経由で呼ばれた場合」しか呼び出すことが出来ません。

また、mutable修飾子がついていない変数への代入を行うこともできないため、constキーワードがついたメンバ関数内では変数の値の変更が行われないということも伝えることができます。

class MyClass {
public:
    void func() const {
        // オブジェクト自身やそのメンバー変数等読み込み専用処理
    }
};

constポインタ

ポインタ自体もconst指定子(*) をつける事が可能です。

これらのポインタでは指し示すアドレス の書き換え禁止です。ただしアドレスが指す先のデータ自体は書き換え可能です。 以下は例です。

int num = 10;
int* const ptr = # // ポインタptr自体へ代入不可だが、numへ代入可能
*ptr = 20; // ポインタが指すnumへの代入なのでOK
// ptr = &another_num; // ポインタのアドレスの書き換えはでいないためコンパイルエラー

構文が型名* const 変数名となっている点に注意してください。const 型名* 変数名にすると、ポインタにアドレスを再代入して参照先を変更できますが、値の変更ができなくなります。

const参照

参照もまた、参照先データ(オブジェクト) の書き換え禁止指定子(&) をつける事も可能です。 以下は例です。

int num = 10;
const int& ref1 = num; // 参照ref1経由ではnum書き換え不可 
// ref1++; // NG: 参照先データ(num) 書き換え不可 
num++; // OK: 参照(ref1) 経由しなくてもOK 
cout << "ref1=" << ref1 << endl; // OK: ref1=11 

constの利点

constは、プログラムに多くの利点をもたらします。以下では、その主な利点について説明します。

プログラムの安全性の向上

constは、変数や関数パラメーターが変更されることを防ぐことができます。これにより、プログラムの安全性が向上し、バグを減らすことができます。

例えば、以下のコードを考えてみましょう。

void foo(int* x)
{
    *x = 5;
}

int main()
{
    int x = 10;
    foo(&x);
    return 0;
}

このコードでは、foo関数内でポインターxが指す値を変更しています。

しかし、このような変更は予期しない副作用を引き起こす可能性があります。例えば、別の場所で同じポインターを使用している場合に問題が発生する可能性があります。

これを回避するためには、foo関数内でポインターxが指す値を変更しないようにする必要があります。

void foo(const int* x)
{
    // *x = 5; // コンパイルエラー
}

int main()
{
    int x = 10;
    foo(&x);
    return 0;
}

このようにconst 型名* 引数名することで、foo関数内でポインターxが指す値を変更することはできなくなります。これにより、プログラムの安全性が向上します。

最適化の促進

constは処理速度の最適化に役立ちます。

コンパイラーは定数式や不変式(immutable expression)から計算結果をキャッシュしたり、コード生成時に最適化したりすることができます。

例えば、

const double pi = 3.14159265358979323846;

このような定数piはコンパイル時に最適化されて、処理速度が上がる可能性があります。

for (int i = 0; i < n; ++i)
{
    const double val = array[i];
}

このようなループ内部で使用される定数valも最適化される可能性が高くなります。

コードの可読性の向上

constはコードの可読性も向上させます。

const修飾子を使用することで、「この値は変更されません」という意図を明確に示すことができます。また、関数パラメーターや戻り値に対しても同様です。

例えば、

double calculate_area(const double radius)
{
    const double pi = 3.14159265358979323846;
    return pi * radius * radius;
}

このような関数では、radiusおよびpi両方とも定数です。

これら2つの値は関数内で変更される処理がありません。その場合はconst修飾子を使用して明示的に示すことで、関数内のコードに変更を加えるプログラマ、関数を使用するプログラマにも、radiuspiは変更されないということを伝えることができます。

以上がC++言語におけるconst修飾子の主な利点です。

目次