Menu

C++ のクラス: オブジェクト・メンバー・メソッドを定義する

C++ のクラスがデータと振る舞いを再利用可能な型にまとめる方法を学びます。メンバー変数とメソッドの宣言、オブジェクトの生成、public/private の使い分け、そして未初期化メンバーや this ポインタといった落とし穴まで。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

クラスとは何か

クラスは、データ(メンバー変数)と振る舞い(メンバー関数)を 1 つの単位にまとめる独自の型の設計図です。intdouble のような組み込み型が単一の値を表すのに対し、クラスを使えば、銀行口座・2D の点・プレイヤーといった概念全体を、持ち回せる 1 つの値としてモデル化できます。

あなたは自分でクラスを定義しなくても、すでにクラスを使っています。std::stringstd::vector は標準ライブラリのクラスです。name.length()v.push_back(3) を呼ぶのは、オブジェクトに対してメンバー関数を呼んでいるということです。これからは同じやり方で自分の型を作り、その後それらを初期化するためにコンストラクタへ進みます。

クラスの定義とオブジェクトの生成

クラス定義はメンバーを波かっこの中に並べ、セミコロンで終わります。末尾の ; を忘れるのは、初心者が最もよくやる間違いの 1 つです。クラスから生成する各オブジェクトは、メンバー変数の独自のコピーを持ちます。

rexluna は別々の 2 つのオブジェクトです。rex.name を変えても luna.name には影響しません。各オブジェクトは自分のデータを保持します。メンバーにはオブジェクトに対してドット演算子 . でアクセスします(ポインタを持っている場合は ->)。

public と private

既定では class の中身はすべて private です。クラス自身のメンバー関数だけがそれに触れられます。public: ラベルはメンバーを外部のコードに公開します。この区別がカプセル化の核心です。安全なインターフェースを公開し、内部データを隠すことで、不正な状態に陥らないようにします。

count は private なので、呼び出し側は負の値や無意味な値を忍び込ませることができません。必ず increment() を通す必要があります。value() のパラメータリストの後ろにある const は、その関数がオブジェクトを変更しないことを約束します。これにより const オブジェクトに対しても呼び出せるようになり、読み手に意図を伝えます。publicprivateprotected の全容についてはアクセス指定子を参照してください。

メソッドの宣言と定義

小さなクラスでは、(上のように)メソッドをインラインで定義して構いません。大きなクラスでは、メソッドをクラスの中で宣言し、本体は ClassName::method というスコープ構文を使ってクラスの外で定義するのが一般的です。こうするとクラス定義がインターフェースの要約として読みやすく保たれます。

Rectangle:: という接頭辞は、その関数がどのクラスに属するかをコンパイラに伝えます。このような定義の中でも、メンバーは素の名前(widtharea())で参照します。コンパイラは、それらがメソッドの呼び出し対象であるオブジェクトに属していると知っています。

this ポインタ

非静的なメンバー関数の内部では、this は関数が呼び出された対象のオブジェクトを指すポインタです。通常は必要ありません。素のメンバー名がすでに現在のオブジェクトを指しているからです。本領を発揮するのは、パラメータがメンバーをシャドーイングする(同じ名前を共有する)ときで、曖昧さを解消しなければならない場合です。

this-> がないと、setX の中で x = x; と書くとパラメータを自分自身に代入するだけで、メンバーは変わらないまま残ります。これは静かなバグです。this->x はそれを明確にします。多くのコードベースは、パラメータに別の名前を付ける(例: setX(int newX))ことでこの問題を完全に避けますが、実際のコードでは this-> を絶えず目にすることになります。

よくある間違い

クラスにまつわるいくつかの落とし穴は、繰り返し人々を捕らえます。

  • 閉じるセミコロンを忘れる。 クラス定義は }; で終わります。; を落とすと、クラス自身ではなくクラスのに来るものを指す、紛らわしいエラーの洪水が起きます。
  • メンバーの初期化を忘れる。 intdouble のような組み込み型のメンバーは自動的にゼロにされません。未初期化のメンバーを読むのは未定義動作です。メンバーに既定値を与えるか(int count = 0;)、コンストラクタで初期化しましょう。
  • 外部から private メンバーにアクセスする。 private メンバーに対する c.count = 5; はコンパイルエラーです。代わりに public なメソッドを通しましょう。これは意図どおりに機能しているカプセル化です。
  • クラスとオブジェクトを混同する。 Dog.bark(); は誤りです。Dog は型です。メソッドはオブジェクトに対して呼びます: rex.bark();
// 未初期化メンバー - 'age' を読むのは未定義動作:
class Cat {
public:
    int age;            // 既定値なし
};

Cat c;
std::cout << c.age;     // ゴミ値であり、0 ではない

次へ: コンストラクタ

オブジェクトを生成した後で各メンバーを手作業で設定するのは - rex.name = ...; rex.age = ...; - 面倒で忘れやすく、まさにそうやって未初期化メンバーが生まれます。次のページではコンストラクタを扱います。オブジェクトの生成時に自動的に実行される特別な関数で、Dog rex("Rex", 4); というすっきりした 1 行の構文で、すべてのオブジェクトが有効な状態から始まることを保証できます。

よくある質問

C++ におけるクラスとオブジェクトの違いは何ですか?

クラスは設計図です。型がどんなデータ(メンバー変数)とどんな振る舞い(メンバー関数)を持つかを記述します。オブジェクトはその設計図から作られた具体的なインスタンスです。class Dog { ... }; は型を一度だけ定義し、Dog rex; は実際に使える Dog を生成します。1 つのクラスから互いに独立した多数のオブジェクトを作れます。

C++ における class と struct の違いは何ですか?

技術的には既定のアクセスレベルだけが違います。class のメンバーは既定で privatestruct のメンバーは既定で public です。どちらもメンバー関数・コンストラクタ・継承を持てます。慣習として、struct は単純なデータのまとまりに、class は振る舞いや守るべき不変条件を持つ型に使われます。

C++ のクラスで this ポインタは何をするものですか?

メンバー関数の内部では、this は関数が呼び出された対象のオブジェクトを指すポインタです。パラメータとメンバーを区別したり(this->x = x;)、現在のオブジェクトを返したりするのに使います。通常のメンバーアクセスではほとんど必要ありません。x と書けば、すでに現在のオブジェクトの x を指しています。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める