ホームに戻る
出典 :
const を使いこなそう - 組込屋
関連 :
static修飾の使い方 コンストラクタ初期化子 [C#]constとreadonly
目次 :

const修飾とは

修飾する対象が 不変 であることを示すこと。
「変更できない( = 変更されてはならない)」ことを明示することで、データの予期しない変更を防ぐことができるとともに、
対象が入力値としてのみ用いられることが判別しやすくなり、コードの安全性・可読性が高まる。
static修飾との混同に注意。

const修飾の類型

ポインタを修飾する

アスタリスクより左に constキーワードを置くと、 当該ポインタが「指し示す先が」読み取り専用となる
const char* p; /* const修飾されたポインタ変数 */ /* 以下の処理は許容されない(コンパイルが通らない) */ *p = '\0'; p[15] = 0;

参照を修飾する

ポインタ修飾と同様、 参照変数の「指し示す先が」読み取り専用となる
// lhs , rhs の値が関数内で書き換えられないことが保証される TimeSpan subtract(const DateTime &lhs, const DateTime &rhs);

永続的な変数を修飾する

グローバル変数、およびstatic修飾されたローカル変数をconst修飾すると、単に代入ができないだけでなく、 当該変数がROMに配置されるようになる
RAM(スタック)の使用量を削減することができる
/* const修飾されているため、ROMに配置される */ const short tbl_1[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; /* const修飾が無いため、RAMを使用する */ short tbl_2[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
定数配列(テーブル)はconst修飾しなければ、RAMを大量に消費することになる。
特に組み込み用途においてはRAMに余裕が無いことが多いため、「テーブルはconst修飾」は鉄則として理解されたい。

永続的変数がポインタの場合

constキーワードを 「アスタリスクの右」に置かないとROM配置の指示とはならない
IPortOutput* const g_nanaseg1[8] = { &g_port20, &g_port21, &g_port40, &g_port41, &g_port76, &g_port77, &g_port34, &g_port35, };

メンバ変数を修飾する

class Real { private: const double m_val; //< const修飾されたメンバ変数 public: Real(double val) : m_val(val) { } //< m_val は初期化子リストにて初期化されなければならない };
クラスのメンバ変数をconst修飾した場合、 当該変数はコンストラクタ初期化子を用いて「必ず」初期化されなければならなくなる
生成時点で値が定められ(「不定」が認められず)、かつ生涯不変の「性質」を設定する際に有効。

const修飾を用いずに記述した場合は以下のようになる。
class Real { private: double m_val; public: Real(double val) { m_val = val; } //< m_val をコンストラクタ中で初期化(厳密には代入)することは必須ではない };
この場合、 m_val がインスタンス生成時に初期化されるかどうかはコンストラクタの実装(≒良心)に依存し、
またインスタンスの生存期間中に値が更新されてもエラーとはならない。
即ち、「生成時に値が定まっていること」、および「生涯不変であること」が保証されない。
const修飾を用いることで、生涯不変の「性質」と、変化し得る「状態」を区分できる。

コンストラクタ初期化子についてはコンストラクタ初期化子を参照のこと。

メンバ関数を修飾する

class File { public: bool isEnd() const; char get() const; };
引数リスト(括弧)の右にconstを置く
const修飾されたメンバ関数は、メンバ変数を更新することができなくなる。
インスタンスの「状態」が(暗黙的に)変更されることが無いことが保証される。
ここで、 const修飾されたメンバ関数からは、同様にconst修飾されたメンバ関数しかコールすることができない 点に留意されたい。

一時的な変数を修飾する

関数の引数、およびstatic修飾の無いローカル変数(自動変数)に対するconst修飾は、単に「代入を許容しない」という意味となる。
// 引数および自動変数をconst修飾 bool isClockwise(const uint32_t a, const uint32_t b) { const uint32_t diff = b - a; //< 初期化(代入ではない)は可 return (diff & 0x80000000U) != 0; }

constexpr (C++11)

常に同じ結果を返し、副作用の無い(スコープ外の変数やメモリ等を操作しない)式を定数式と呼ぶ
( 3 + 4 など定数同士の演算、または単項の定数。)
定数式はコンパイラの最適化の対象となり、多くの場合でコンパイル時に演算が行われ、プログラム中にはその結果のみが格納される。
(即ちプログラム中の定数式 3 + 4 は、コンパイルにより定数 7 に置換される。)
但し、関数呼び出しやオブジェクトコンストラクタが出現すると、その結果がコンパイル時に確定していても定数式とは見なされなくなる。
// C++03まで (有効でないコード) int get5() { return 5; } int arr_values[get5() + 5]; //< 要素数10の配列を作りたいが、 //< get5() が定数式と見なされないため不可 ⇒ コンパイルエラー
C++11ではキーワード constexpr が導入され、関数やオブジェクトコンストラクタが定数式(コンパイル時定数)であることを保証できるようになった。
即ち、下記の get5() は定数式と解釈される
// C++11以降 (有効なコード) constexpr int get5() { return 5; } //< constexpr 修飾により get5() は定数式となる int arr_values[get5() + 5]; //< 要素数10の配列を作る ⇒ []内が定数式のため有効
なお、この constexpr は const の代替として、定数を定数式として定義する際に用いることも可能である。
constexpr double forceOfGravity = 9.8; constexpr double moonGravity = forceOfGravity / 6;