
C++入門|const修飾子
変えてよい力と、守るべき力を明確にする。constは、戦士の重要なデータを誤操作から守る変更禁止の封印です。
C++で複数のクラスや関数が協力するプログラムを作るようになると、同じオブジェクトをさまざまな場所から利用するようになります。
たとえば、戦士の情報を管理するクラスを、修行装置、戦闘力測定器、回復装置などへ渡す場面を考えてみましょう。
戦闘力測定器は、戦士の現在の状態を読み取るだけで十分です。ところが、渡されたオブジェクトを自由に変更できる設計になっていると、測定するだけのはずの処理が、戦士の気力や技名を書き換えてしまう可能性があります。
そこで役立つのがconst修飾子です。
constは、値やオブジェクトに対して、ここでは変更してはいけないという制限を付けるために使います。
ドラゴンボール風にたとえるなら、constは神殿の重要な修行記録にかける保護結界です。
記録を読むことはできますが、勝手に書き換えることはできません。変更しようとすると、コンパイラが危険を知らせて処理を止めてくれます。
constは、主に次のような場所で使われます。
| constを使う場所 | 主な役割 |
|---|---|
| 変数 | 初期化後の値を変更できないようにする |
| ポインタや参照の引数 | 関数内から対象のオブジェクトを変更できないようにする |
| メンバ関数の末尾 | その関数内でオブジェクトの状態を変更できないようにする |
| staticメンバ変数 | クラス全体で共有する変更不可能な値を定義する |
constを正しく使うと、プログラムを読む人にも、コンパイラにも、どのデータを変更してよいのかを明確に伝えられます。
const修飾子とは
constは、constantをもとにしたキーワードです。
変数の前にconstを付けると、その変数は初期化後に変更できなくなります。
const int MAX_ENERGY = 100;このMAX_ENERGYには100が設定されています。
初期化後に、別の値を代入することはできません。
MAX_ENERGY = 120;この処理はコンパイルエラーになります。
MAX_ENERGYは、最大気力というプログラム全体で意味を持つ値です。途中で120や150へ変わってしまうと、気力計算のルールが崩れてしまいます。
そこでconstを付け、変更できない値として管理します。
図:constは値を守る変更禁止の封印

この図が示していること
この図では、constを付けた値が保護されていることを表しています。
MAX_ENERGYの値を読み取ることはできます。しかし、初期化後に別の値を代入することはできません。
変更しようとした時点でコンパイラがエラーを出すため、プログラムを実行する前に誤りを発見できます。
constを付けた変数
通常の変数は、プログラムの途中で値を変更できます。
int energy = 50;
energy = 80;energyは通常の変数なので、50から80へ変更できます。
一方、constを付けた変数は変更できません。
const int MAX_ENERGY = 100;次の代入は許可されません。
MAX_ENERGY = 120;const変数は、宣言するときに初期値を与えるのが基本です。
const int MAX_ENERGY = 100;次のように、初期値を与えずに宣言することはできません。
const int MAX_ENERGY;あとから値を設定できないため、宣言時に値が決まっていなければならないからです。
| 変数 | あとから変更できるか | 用途 |
|---|---|---|
| int energy = 50 | できる | 現在の気力など、変化する値 |
| const int MAX_ENERGY = 100 | できない | 最大値など、変更させたくない値 |
ドラゴンボール風に考えると、energyは修行によって増減する現在の気力です。
MAX_ENERGYは神殿が定めた最大気力の上限です。現在の気力は変化しますが、最大値の規則は勝手に変更してはいけません。
constを使うと意図が伝わりやすくなる
次の変数を見てみましょう。
int maxEnergy = 100;変数名から最大気力を表していることは分かりますが、プログラム上は変更できます。
一方、次のように書くと、変更してはいけない値であることが明確になります。
const int MAX_ENERGY = 100;constを付けることで、プログラムを読む人にも、この値は途中で変更しないという設計上の意図を伝えられます。
コンパイラも変更を禁止してくれるため、コメントで注意を促すだけよりも安全です。
関数の引数をconst指定する
constは、関数の引数にも付けられます。
特に大切なのが、ポインタや参照を使ってオブジェクトを渡す場合です。
通常の参照でオブジェクトを受け取ると、関数内から呼び出し元のオブジェクトを変更できます。
void changeEnergy(WarriorProfile& profile);一方、const参照で受け取ると、関数内からオブジェクトを変更できなくなります。
void showProfile(const WarriorProfile& profile);このprofileは、読み取り専用として扱われます。
オブジェクト全体をコピーせずに受け取れますが、そのオブジェクトの状態を変更する処理は行えません。
const参照
クラスのオブジェクトを読み取り専用で受け取りたい場合は、const参照がよく使われます。
void showProfile(const WarriorProfile& profile);この宣言を分解すると、次のようになります。
| 部分 | 意味 |
|---|---|
| const | 対象のオブジェクトを変更しない |
| WarriorProfile | 受け取るクラスの型 |
| & | 参照として受け取る |
| profile | 仮引数名 |
参照で受け取るため、WarriorProfileオブジェクト全体のコピーを作る必要がありません。
さらにconstが付いているため、関数内から元のオブジェクトを書き換えることもできません。
つまり、const参照には次の2つの役割があります。
| 役割 | 内容 |
|---|---|
| コピーを避ける | 大きなオブジェクトを効率よく受け渡せる |
| 変更を禁止する | 関数内から呼び出し元の状態を変更できない |
constポインタ
ポインタを使って読み取り専用のオブジェクトを受け取ることもできます。
void inspectProfile(const WarriorProfile* profile);この場合、profileが指しているWarriorProfileオブジェクトを、関数内から変更できません。
profile->showStatus();状態を表示するようなconstメンバ関数は呼び出せます。
一方、状態を変更するメンバ関数は呼び出せません。
profile->setEnergy(100);setEnergyが通常のメンバ関数であれば、const WarriorProfile*を通して呼び出すことはできません。
const参照とconstポインタの基本的な違いは次のとおりです。
| 比較項目 | const参照 | constポインタ |
|---|---|---|
| 宣言 | const WarriorProfile& profile | const WarriorProfile* profile |
| 呼び出し | showProfile(profile) | inspectProfile(&profile) |
| メンバアクセス | profile.showStatus() | profile->showStatus() |
| nullの可能性 | 基本的に対象が必要 | nullptrの可能性がある |
| 主な使い方 | 必ず存在するオブジェクトを読む | 対象が存在しない可能性も表現する |
値渡しの引数に付けたconst
値渡しの引数にもconstを付けられます。
void setLevel(const int level);この場合、関数内でlevelを変更できません。
void setLevel(const int level) {
level = 10;
}この代入はコンパイルエラーになります。
ただし、値渡しでは最初から引数のコピーが作られます。関数内で引数を書き換えても、呼び出し元の変数には影響しません。
そのため、intのような小さな型では、次のようにconstを付けずに受け取ることも一般的です。
void setLevel(int level);一方、stringやvector、自作クラスなどを読み取り専用で受け取る場合は、const参照がよく使われます。
void setTechniqueName(const string& techniqueName);この書き方なら、文字列全体のコピーを避けながら、関数内で引数を書き換えないようにできます。
constメンバ関数
クラスのメンバ関数にもconstを付けられます。
メンバ関数をconst指定する場合は、引数リストの後ろにconstを書きます。
int getEnergy() const;このconstは、getEnergyの中でオブジェクトの状態を変更しないことを表します。
たとえば、次の関数は気力を返すだけです。
int WarriorProfile::getEnergy() const {
return m_energy;
}m_energyを読み取っていますが、変更はしていません。
そのため、constメンバ関数として定義できます。
一方、次の関数はm_energyを変更するため、constを付けられません。
void WarriorProfile::setEnergy(int energy) {
m_energy = energy;
}| メンバ関数 | 状態を変更するか | constを付けるか |
|---|---|---|
| getEnergy | 変更しない | 付ける |
| getTechniqueName | 変更しない | 付ける |
| showStatus | 変更しない | 付ける |
| setEnergy | 変更する | 付けない |
| setTechniqueName | 変更する | 付けない |
図:const参照とconstメンバ関数の連携

この図が示していること
この図では、WarriorProfileオブジェクトをconst参照で関数へ渡す流れを表しています。
const参照を通してオブジェクトを利用する場合、getEnergyやshowStatusのようなconstメンバ関数は呼び出せます。
一方、setEnergyやsetTechniqueNameのようにオブジェクトの状態を変更するメンバ関数は呼び出せません。
const参照とconstメンバ関数を組み合わせることで、読み取り専用の安全な処理を作れます。
constを使ったクラスを作成する
ここでは、戦士の技名と現在の気力を管理するWarriorProfileクラスを作成します。
このクラスでは、次の場所でconstを使用します。
| 使用場所 | 使用例 |
|---|---|
| staticメンバ変数 | static const int MAX_ENERGY |
| setterの引数 | const string& techniqueName |
| getter | string getTechniqueName() const |
| 状態取得 | int getEnergy() const |
| 状態表示 | void showStatus() const |
| 読み取り専用の関数引数 | const WarriorProfile& profile |
WarriorProfileクラスのヘッダーファイル
プロジェクト/ファイル名: Chap7_03/warrior_profile.h
#ifndef CHAP7_03_WARRIOR_PROFILE_H
#define CHAP7_03_WARRIOR_PROFILE_H
#include <string>
// 戦士の技名と気力を管理するクラス
class WarriorProfile {
private:
// 戦士が使用する技名
std::string m_techniqueName;
// 戦士の現在の気力
int m_energy;
public:
// クラス全体で共有する最大気力
static const int MAX_ENERGY = 100;
// コンストラクタ
WarriorProfile();
// 技名を設定する
void setTechniqueName(const std::string& techniqueName);
// 技名を取得する
std::string getTechniqueName() const;
// 気力を設定する
void setEnergy(int energy);
// 現在の気力を取得する
int getEnergy() const;
// 戦士の状態を表示する
void showStatus() const;
};
#endifヘッダーファイルでは、MAX_ENERGYをstatic constメンバ変数として宣言しています。
static const int MAX_ENERGY = 100;staticが付いているため、MAX_ENERGYは各オブジェクトが個別に持つ値ではありません。
WarriorProfileクラス全体に1つだけ存在する共有データです。
constも付いているため、値を変更できません。
static constメンバ変数
MAX_ENERGYにはstaticとconstの両方が付いています。
static const int MAX_ENERGY = 100;それぞれの役割は次のとおりです。
| 修飾子 | 役割 |
|---|---|
| static | オブジェクトごとではなく、クラス全体で共有する |
| const | 初期化後に値を変更できないようにする |
staticメンバはクラスに属するため、クラス名からアクセスする書き方が分かりやすいです。
WarriorProfile::MAX_ENERGYオブジェクトを作らなくても利用できます。
cout << WarriorProfile::MAX_ENERGY << endl;オブジェクトからアクセスすることもできますが、staticメンバであることが分かりやすいように、クラス名からアクセスするのが一般的です。
WarriorProfileクラスの処理を実装する
プロジェクト/ファイル名: Chap7_03/warrior_profile.cpp
#include "warrior_profile.h"
#include <iostream>
using namespace std;
// 古いC++規格でも利用できるように実体を定義する
const int WarriorProfile::MAX_ENERGY;
// コンストラクタ
WarriorProfile::WarriorProfile()
: m_techniqueName("未設定"), m_energy(0) {
}
// 技名を設定する
void WarriorProfile::setTechniqueName(
const string& techniqueName) {
m_techniqueName = techniqueName;
// const参照なので、次の変更はできない
// techniqueName = "別の技";
}
// 技名を取得する
string WarriorProfile::getTechniqueName() const {
// constメンバ関数なので、次の変更はできない
// m_techniqueName = "変更された技";
return m_techniqueName;
}
// 気力を設定する
void WarriorProfile::setEnergy(int energy) {
// 0未満の値は0に修正する
if (energy < 0) {
m_energy = 0;
}
// 最大気力を超えた値はMAX_ENERGYに修正する
else if (energy > MAX_ENERGY) {
m_energy = MAX_ENERGY;
}
else {
m_energy = energy;
}
}
// 現在の気力を取得する
int WarriorProfile::getEnergy() const {
return m_energy;
}
// 戦士の状態を表示する
void WarriorProfile::showStatus() const {
cout << "技名:" << m_techniqueName << endl;
cout << "現在気力:" << m_energy << endl;
cout << "最大気力:" << MAX_ENERGY << endl;
}const参照を使ったsetter
setTechniqueNameでは、引数をconst参照で受け取っています。
void WarriorProfile::setTechniqueName(
const string& techniqueName)stringを値渡しすると、関数へ渡すときに文字列のコピーが作られます。
void setTechniqueName(string techniqueName);const参照なら、元の文字列を参照するため、文字列全体のコピーを避けられます。
void setTechniqueName(const string& techniqueName);さらにconstが付いているため、関数内でtechniqueNameを書き換えることはできません。
techniqueName = "別の技";この処理はコンパイルエラーになります。
ただし、引数の値をメンバ変数へコピーすることはできます。
m_techniqueName = techniqueName;禁止されているのは、引数が参照している元の文字列を変更することです。
constメンバ関数を使ったgetter
getTechniqueNameの末尾にはconstが付いています。
string getTechniqueName() const;関数定義にも同じようにconstを付けます。
string WarriorProfile::getTechniqueName() const {
return m_techniqueName;
}この関数は、m_techniqueNameを返すだけです。
オブジェクトの状態を変更しないため、constメンバ関数として定義できます。
関数内でm_techniqueNameを変更しようとすると、コンパイルエラーになります。
m_techniqueName = "変更された技";constメンバ関数では、通常のメンバ変数を変更できません。
constメンバ関数とthisポインタ
通常のメンバ関数では、thisは現在のオブジェクトを指すポインタです。
概念的には、次のような型として扱われます。
WarriorProfile* const thisthis自体が指す先を変更できませんが、thisが指しているオブジェクトの状態は変更できます。
一方、constメンバ関数のthisは、概念的に次のように考えられます。
const WarriorProfile* const thisthis自体の指す先を変更できないだけでなく、thisが指しているWarriorProfileオブジェクトも変更できません。
そのため、constメンバ関数内では、通常のメンバ変数を書き換えられません。
main関数で動作を確認する
main.cppでは、WarriorProfileオブジェクトを作成します。
最大気力を表示したあと、技名と現在気力を設定します。
最後に、const参照を受け取るshowReadOnlyProfileへオブジェクトを渡します。
プロジェクト/ファイル名: Chap7_03/main.cpp
#include <iostream>
#include "warrior_profile.h"
using namespace std;
// 戦士の情報を読み取り専用で受け取る関数
void showReadOnlyProfile(const WarriorProfile& profile);
int main(int argc, char** argv) {
// クラス全体で共有する定数を表示する
cout << "最大気力の定数:"
<< WarriorProfile::MAX_ENERGY << endl;
// 戦士情報を管理するオブジェクト
WarriorProfile warrior;
// 技名と現在気力を設定する
warrior.setTechniqueName("蒼天気功");
warrior.setEnergy(85);
// const参照で戦士情報を表示する
showReadOnlyProfile(warrior);
return 0;
}
// 渡されたオブジェクトの情報を読み取る
void showReadOnlyProfile(const WarriorProfile& profile) {
cout << "==== 戦士情報 ====" << endl;
// constメンバ関数なので呼び出せる
profile.showStatus();
// 状態を変更する関数は呼び出せない
// profile.setEnergy(100);
cout << "==================" << endl;
}実行結果
最大気力の定数:100
==== 戦士情報 ====
技名:蒼天気功
現在気力:85
最大気力:100
==================プログラムの流れ
プログラムでは、最初にMAX_ENERGYをクラス名から表示しています。
WarriorProfile::MAX_ENERGYMAX_ENERGYはstaticメンバなので、WarriorProfileオブジェクトを作る前でも利用できます。
次に、WarriorProfileオブジェクトを作成します。
WarriorProfile warrior;技名を設定します。
warrior.setTechniqueName("蒼天気功");現在気力を設定します。
warrior.setEnergy(85);最後に、warriorをconst参照でshowReadOnlyProfileへ渡します。
showReadOnlyProfile(warrior);showReadOnlyProfileの仮引数は次の形です。
const WarriorProfile& profileそのため、profileを通してwarriorの情報を読み取ることはできますが、変更はできません。
const参照から呼び出せるメンバ関数
showReadOnlyProfileのprofileは、const WarriorProfile&です。
そのため、constメンバ関数は呼び出せます。
profile.showStatus();showStatusは次のようにconst指定されています。
void showStatus() const;一方、setEnergyはオブジェクトの状態を変更するため、constメンバ関数ではありません。
void setEnergy(int energy);そのため、次の呼び出しはできません。
profile.setEnergy(100);コンパイラは、読み取り専用のprofileを通してオブジェクトを変更しようとしていると判断し、エラーを出します。
constオブジェクト
オブジェクトそのものをconst指定することもできます。
const WarriorProfile warrior;constオブジェクトは、生成後に状態を変更できません。
そのため、constメンバ関数だけを呼び出せます。
warrior.showStatus();showStatusはconstメンバ関数なので呼び出せます。
一方、次の関数は呼び出せません。
warrior.setEnergy(80);setEnergyはm_energyを変更する関数だからです。
constオブジェクトとメンバ関数の関係は、次のように整理できます。
| オブジェクト | constメンバ関数 | 通常のメンバ関数 |
|---|---|---|
| 通常のオブジェクト | 呼び出せる | 呼び出せる |
| constオブジェクト | 呼び出せる | 呼び出せない |
| const参照 | 呼び出せる | 呼び出せない |
| constポインタ | 呼び出せる | 呼び出せない |
constメンバ関数の宣言と定義
constメンバ関数では、ヘッダーファイルの宣言とcppファイルの定義の両方にconstが必要です。
ヘッダーファイルでは、次のように書きます。
int getEnergy() const;cppファイルでは、次のように書きます。
int WarriorProfile::getEnergy() const {
return m_energy;
}宣言にだけconstを付け、定義からconstを省略すると、別の関数として扱われてしまうため、正しく定義できません。
| 場所 | 正しい記述 |
|---|---|
| クラス内の宣言 | int getEnergy() const; |
| cppファイルの定義 | int WarriorProfile::getEnergy() const |
constの位置で意味が変わるポインタ
ポインタでは、constを書く位置によって意味が変わります。
指している値を変更できないポインタ
const int* power;または次のようにも書けます。
int const* power;この場合、powerが指している値を変更できません。
ただし、power自身が別の変数を指すように変更することはできます。
ポインタ自身を変更できない場合
int* const power = &energy;この場合、powerが指しているenergyの値は変更できます。
ただし、powerを別の変数へ向けることはできません。
値もポインタも変更できない場合
const int* const power = &energy;この場合、指している値も、ポインタの指す先も変更できません。
| 宣言 | 指している値 | ポインタの指す先 |
|---|---|---|
| const int* power | 変更できない | 変更できる |
| int* const power | 変更できる | 変更できない |
| const int* const power | 変更できない | 変更できない |
図:constを使う場所と守られる範囲

この図が示していること
この図では、constを付ける場所によって、何が保護されるのかを整理しています。
変数へconstを付けると、その値を変更できません。
ポインタや参照が指す型へconstを付けると、そのポインタや参照を通して対象のオブジェクトを変更できません。
メンバ関数の末尾へconstを付けると、その関数内でオブジェクトの状態を変更できません。
static constメンバを使うと、クラス全体で共有する変更不可能な値を定義できます。
constメンバ関数内で変更しようとした場合
次のshowStatusはconstメンバ関数です。
void WarriorProfile::showStatus() const {
cout << m_energy << endl;
}この関数内でm_energyを変更しようとすると、コンパイルエラーになります。
void WarriorProfile::showStatus() const {
m_energy = 100;
}showStatusは読み取り専用の関数として宣言されているためです。
同じように、constメンバ関数から通常のメンバ関数を呼び出すことも、基本的にはできません。
void WarriorProfile::showStatus() const {
setEnergy(100);
}setEnergyはm_energyを変更する可能性があるため、constメンバ関数からは呼び出せません。
一方、別のconstメンバ関数は呼び出せます。
void WarriorProfile::showStatus() const {
cout << getEnergy() << endl;
}getEnergyもconstメンバ関数なので、安全に呼び出せます。
const参照で引数を受け取る利点
クラスオブジェクトを関数へ渡す場合、値渡しではオブジェクトのコピーが作られます。
void showProfile(WarriorProfile profile);WarriorProfileが多くのメンバ変数を持つ大きなクラスだった場合、コピーにも処理が必要です。
const参照なら、コピーを作らずに元のオブジェクトを参照できます。
void showProfile(const WarriorProfile& profile);さらに、constによって関数内からの変更も禁止できます。
| 引数の渡し方 | コピー | 元のオブジェクトの変更 |
|---|---|---|
| WarriorProfile profile | 作られる | 変更されない |
| WarriorProfile& profile | 作られない | 変更できる |
| const WarriorProfile& profile | 作られない | 変更できない |
読み取るだけの関数では、const参照が安全で効率的な選択になります。
constを使う意味
constの最も大きな役割は、変更してはいけないデータをプログラム上で明確にすることです。
コメントで値を変更しないでくださいと書くだけでは、コンパイラは変更を止めてくれません。
constを付けておけば、誤って変更するコードを書いた時点でコンパイルエラーになります。
| constによる効果 | 内容 |
|---|---|
| 誤操作の防止 | 変更してはいけない値への代入を禁止する |
| 意図の明確化 | 読み取り専用であることをコード上で示す |
| 関数の役割を明確にする | 状態を読む関数と変更する関数を区別できる |
| constオブジェクトへの対応 | 読み取り専用オブジェクトから安全に呼び出せる |
| 不要なコピーの回避 | const参照によって大きなオブジェクトを効率よく渡せる |
constと処理速度の関係
constを付けると必ず処理速度が向上する、というわけではありません。
constは、主にプログラムの安全性と設計上の意図を明確にするための機能です。
コンパイラがconstの情報を最適化に利用できる場合もありますが、constを付けただけで必ず高速になるとは限りません。
特に大きなオブジェクトをconst参照で渡す場合は、値渡しによるコピーを避けられるため、処理量やメモリ使用量を減らせる可能性があります。
void showProfile(const WarriorProfile& profile);この場合の効率向上は、constそのものよりも、参照によってコピーを避けていることが大きな理由です。
constを使う第一の目的は、変更してはいけないものを守り、間違いを早い段階で発見することだと考えるとよいでしょう。
constを適切に使う考え方
すべての変数や関数にconstを付ければよいわけではありません。
変更する必要があるものには、通常の変数や通常のメンバ関数を使います。
変更する必要がないものには、constを付けます。
| 状況 | 選ぶ形 |
|---|---|
| 途中で値を変更する | 通常の変数 |
| 初期化後に値を固定する | const変数 |
| オブジェクトを変更する関数 | 通常の参照 |
| オブジェクトを読むだけの関数 | const参照 |
| メンバ変数を変更する関数 | 通常のメンバ関数 |
| メンバ変数を読むだけの関数 | constメンバ関数 |
| クラス全体で共有する固定値 | static constメンバ |
ドラゴンボール風に考えるなら、現在気力は修行や戦闘で変化するため、通常の変数として管理します。
最大気力は戦士の管理規則として固定するため、constで守ります。
気力を回復する処理は状態を変更するため、通常のメンバ関数にします。
気力を表示するだけの処理は、状態を変更しないため、constメンバ関数にします。
const修飾子で身につけたい感覚
constは、値を単に変更できなくするだけの機能ではありません。
その変数、引数、オブジェクト、メンバ関数が、どのような役割を持っているのかを明確にするための仕組みです。
const int MAX_ENERGY = 100;このconstは、MAX_ENERGYが変更されない値であることを表します。
void showProfile(const WarriorProfile& profile);このconstは、showProfileが渡されたWarriorProfileを変更しないことを表します。
int getEnergy() const;このconstは、getEnergyがオブジェクトの状態を変更しないことを表します。
static const int MAX_ENERGY = 100;このstatic constは、クラス全体で共有する変更不可能な値であることを表します。
ドラゴンボール風にたとえるなら、constは重要な気力データや修行記録を守る保護結界です。
見ることはできても、許可なく変更することはできません。
値を変更する必要がある処理と、読み取るだけの処理を明確に分けることで、クラスの役割が分かりやすくなります。
変更してはいけない場所へconstを付ける習慣を身につけると、意図しない値の変更をコンパイル時に発見でき、安全で読みやすいC++プログラムを作れるようになります。
今回の構成では、処理速度に関する説明を断定せず、constの中心的な役割である安全性と設計意図を重視しています。
