C++のメンバ変数とは、クラスや構造体に属する変数のことです。
クラスは「設計図」、オブジェクトは「その設計図から作られた実体」と考えると分かりやすいです。
メンバ変数は、その実体が持つ状態や属性を表します。
たとえば、人を表すクラスであれば、
- 名前
- 年齢
- 身長
のような情報を持たせることがあります。
このような「そのオブジェクトが覚えている情報」がメンバ変数です。
メンバ変数の基本的な役割
メンバ変数の役割は、オブジェクトの状態を保持することです。
同じクラスから複数のオブジェクトを作った場合でも、通常のメンバ変数は、それぞれのオブジェクトごとに別々の値を持ちます。
たとえば、同じ「人」クラスから作られた2人のオブジェクトがいたとしても、1人は20歳、もう1人は25歳、というように個別の情報を保持できます。
この「オブジェクトごとに別の値を持つ」という性質は、通常の非 static メンバ変数に当てはまります。
非 static メンバ変数と static メンバ変数の違い
C++では、メンバ変数は大きく2種類に分けて考えると理解しやすくなります。
非 static メンバ変数
これは一般的なメンバ変数で、各オブジェクトごとに存在する変数です。
オブジェクトの個別の状態を表します。
static メンバ変数
これは各オブジェクトに属するのではなく、クラス全体で共有される変数です。
同じクラスから作られたすべてのオブジェクトが、同じ1つの値を共有します。
たとえば、作成されたオブジェクトの総数を記録したい場合などには、static メンバ変数が向いています。
つまり、メンバ変数という言葉は広く使われますが、厳密には
- オブジェクトごとの状態を持つもの
- クラス全体で共有されるもの
の両方があるという点を押さえておくと理解が深まります。
メンバ変数とローカル変数の違い
メンバ変数と混同しやすいものに、ローカル変数があります。
メンバ変数
- クラスや構造体の中で宣言される
- オブジェクトの状態を表す
- 非
staticの場合、オブジェクトが存在している間は保持される
ローカル変数
- 関数や処理のまとまりの中で宣言される
- 一時的な計算や補助のために使う
- その範囲を抜けると使えなくなる
簡単に言うと、メンバ変数は「そのオブジェクトが長く覚えている情報」、ローカル変数は「その場限りで使う作業用の情報」です。
メンバ変数はオブジェクトの設計に深く関わる
メンバ変数は、単にデータを置く場所ではありません。
そのクラスが「何を表すのか」を決める重要な要素です。
たとえば、銀行口座を表すクラスなら、
- 口座名義
- 残高
などがメンバ変数になりやすいです。
一方で、毎回の計算でしか使わない一時的な数字は、通常はメンバ変数ではなくローカル変数にしたほうが自然です。
つまり、メンバ変数として持たせるべきなのは、そのオブジェクトの本質的な状態です。
アクセス修飾子とメンバ変数
C++では、メンバ変数に対してアクセス修飾子を使います。
代表的なのは次の3つです。
publicprivateprotected
このうち、特に重要なのが public と private です。
public
外部から直接アクセスできます。
分かりやすい反面、クラスの内部状態を好きに書き換えられてしまうため、大きなプログラムでは扱いに注意が必要です。
private
外部から直接アクセスできません。
クラスの内部だけで扱えるようにすることで、不正な値が入るのを防いだり、内部状態を安全に管理したりしやすくなります。
実務では、メンバ変数は private にして、必要な操作だけを公開する設計がよく使われます。
なぜ private にするのか
メンバ変数を外部に公開しすぎると、オブジェクトの状態が簡単に壊れてしまいます。
たとえば、本来は負の値になってはいけないデータがあるとします。
そのメンバ変数を直接触れるようにしてしまうと、外部から不正な値を代入されるかもしれません。
しかし private にしておけば、値を変更するための専用の操作を用意し、その中で妥当性チェックができます。
このように、データを隠し、意味のある操作だけを外に見せる考え方をカプセル化といいます。
getter / setter は必須ではない
入門書ではよく、「private にして getter / setter を作る」と説明されます。
これは基本としては間違っていませんが、常にそれが最善とは限りません。
なぜなら、単に読み書きの窓口を作るだけでは、実質的に公開変数とあまり変わらないことがあるからです。
大切なのは、「そのクラスにとって意味のある操作を公開しているかどうか」です。
たとえば、ある図形の幅と高さを自由に外部から設定させるよりも、「面積を求める」「拡大する」といった意味のある操作を提供するほうが、自然で安全な設計になることがあります。
つまり、メンバ変数の扱いでは、何を見せるかより、どう扱わせるかが重要です。
メンバ変数の初期化はとても重要
C++では、メンバ変数をきちんと初期化することが非常に重要です。
特に整数や小数、真偽値などの基本的な型は、明示的に初期化しないと、期待した値が入っていないことがあります。
初心者がつまずきやすい点のひとつです。
「オブジェクトを作ったら自動的に安全な初期値が入る」と思い込むのは危険です。
C++では、型や初期化方法によって動きが異なるため、必要な値は自分で明示的に初期化するのが基本です。
初期化の方法
メンバ変数の初期化には、主に次の考え方があります。
クラス定義の中で初期値を与える方法
あらかじめ標準的な初期値が決まっている場合に分かりやすく安全です。
コンストラクタで初期化する方法
オブジェクト生成時に、与えられた値を使ってメンバ変数を初期化するやり方です。
C++では非常によく使われます。
メンバ初期化リストによる初期化
これはC++で特に重要な方法です。
コンストラクタ本体の中で後から値を入れるのではなく、最初からその値でメンバ変数を構築する考え方です。
この方法は、効率の面だけでなく、後述する const メンバ変数や参照メンバ変数を正しく扱ううえでも重要です。
代入と初期化は同じではない
初心者が混同しやすい点ですが、C++では初期化と代入は別物です。
- 初期化は、変数が作られるときに最初の値を与えること
- 代入は、すでに存在している変数にあとから値を入れること
メンバ変数については、「最初から適切な値で作る」ほうが自然で安全なことが多いため、C++ではメンバ初期化リストが重視されます。
const メンバ変数
const が付いたメンバ変数は、初期化後に値を変更できません。
そのため、あとから代入するのではなく、オブジェクト生成時に必ず初期化する必要があります。
この性質のため、const メンバ変数はメンバ初期化リストで扱うのが基本です。
参照メンバ変数
参照をメンバ変数にすることもできますが、参照は一度結び付けたあとに別の対象へ付け替えることができません。
そのため、これもやはり、生成時に正しく初期化する必要があります。
つまり、
constメンバ変数- 参照メンバ変数
は、C++において初期化の大切さを理解する代表例です。
static メンバ変数の意味
static メンバ変数は、各オブジェクトの中にそれぞれ存在するのではなく、クラスに属する共有データとして扱われます。
これは、あるオブジェクト固有の情報ではなく、クラス全体に関係する情報を持たせたいときに向いています。
たとえば、
- 作成されたインスタンスの総数
- 全オブジェクトで共通の設定値
- 共通のカウンタ
などです。
通常のメンバ変数との違いは、「個別の状態」か「共有の状態」かにあります。
this とメンバ変数の関係
メンバ関数の中では、現在その関数を呼び出しているオブジェクト自身を指す仕組みがあります。
これが this です。
特に関数の引数名とメンバ変数名が同じ場合には、「どちらがメンバ変数で、どちらが引数なのか」を区別するために使われます。
ただし、設計や書き方によっては、引数名を変えることで分かりやすくすることもあります。
重要なのは、メンバ変数と一時的な引数を明確に区別することです。
class と struct におけるメンバ変数
C++では、class でも struct でもメンバ変数を持つことができます。
この点では両者に大きな差はありません。
主な違いは、デフォルトのアクセス設定です。
classは、何も指定しないとメンバがprivatestructは、何も指定しないとメンバがpublic
さらに、継承のデフォルト設定も異なりますがメンバ変数の入門段階では、まず「初期設定の公開範囲が違う」と理解しておけば十分です。
メンバ変数として他のオブジェクトを持つこともできる
メンバ変数は、整数や文字列のような基本的なデータだけではありません。
別のクラスのオブジェクトをメンバ変数として持つこともできます。
これは、あるものが別のものを内部に含んでいる、という関係を表現するときに便利です。
たとえば、車がエンジンを持つ、家が部屋を持つ、といったような設計です。
この考え方は、クラス設計の基本として非常に重要です。
初期化順序に注意する
C++では、複数のメンバ変数を初期化するとき、実際の初期化順序はクラスの中で宣言された順番で決まります。
コンストラクタで見た目上どう書いているかでは決まりません。
この点を知らないと、「先に初期化されると思っていたものが実は後だった」という誤解が起きることがあります。
そのため、設計上も記述上も、宣言順と初期化の意図をそろえることが大切です。
未初期化メンバ変数は危険
これは特に大事です。
C++では、メンバ変数が自動的に期待どおりの値になるとは限りません。
初期化されていない状態の値を使ってしまうと、不正な動作や予期しない結果の原因になります。
特に基本型のメンバ変数については、「初期化しないと危険」と覚えておくくらいでちょうどよいです。
安全なC++を書くうえでは、作った直後から常に有効な状態にしておくことが重要です。
ポインタや参照をメンバ変数にするときの注意
ポインタや参照をメンバ変数にすると、単なる数値や文字列よりも設計が難しくなります。
なぜなら、それらは別の場所にあるデータとのつながりを表すため、寿命や所有権を意識しなければならないからです。
たとえば、
- その参照先やポインタ先はいつまで有効なのか
- 誰がそのデータを管理しているのか
- 無効な参照やポインタにならないか
といった点を考える必要があります。
現代C++では、必要に応じてスマートポインタを使ったり、そもそも値として保持できないかを検討したりすることが多いです。
メンバ変数とメンバ関数の関係
クラスを理解するうえで大切なのは、メンバ変数とメンバ関数をセットで考えることです。
- メンバ変数は、そのオブジェクトが持っている情報
- メンバ関数は、その情報を使ってできる操作
という関係です。
たとえば、口座クラスなら残高というメンバ変数があり、入金や出金というメンバ関数があるといった形です。
このように、状態と操作をひとまとまりにするのがオブジェクト指向の基本的な考え方です。
実務的な観点で見るメンバ変数
実務では、メンバ変数を増やせばよいというものではありません。
メンバ変数が多すぎるクラスは、責務が重くなり、管理しにくくなることがあります。
そのため、
- 本当にそのクラスが持つべき状態か
- 一時的な処理用の値ではないか
- 外部に見せるべきか、内部だけで管理すべきか
- 複数の変数に矛盾が起きないか
といった点を考える必要があります。
つまり、メンバ変数は「ただの保存場所」ではなく、クラス設計そのものに深く関わる要素です。
まとめ
C++のメンバ変数とは、クラスや構造体に属する変数であり、オブジェクトの状態を表すものです。
特に重要なポイントを整理すると、次のようになります。
- 通常の非
staticメンバ変数は、オブジェクトごとに個別に存在する staticメンバ変数は、クラス全体で共有される- メンバ変数はオブジェクトの状態を保持する
privateにして必要な操作だけ公開する設計がよく使われる- 初期化は非常に重要で、未初期化のまま使うのは危険
constメンバ変数や参照メンバ変数では、特に初期化が重要- メンバ変数はクラス設計の中心であり、何を持たせるかをよく考える必要がある
覚え方としては、
- メンバ変数は、そのオブジェクトが覚えている情報
- メンバ関数は、そのオブジェクトができること
と考えると理解しやすいです。
以上、C++のメンバ変数についてでした。
最後までお読みいただき、ありがとうございました。
