🎮 第5章 動くオブジェクトを作ろう:Playerクラス(外部定義編)
🏁 学習目標
- クラスの外でメンバ関数を定義する方法を学ぶ
- 「スコープ解決演算子(::)」の意味を理解する
- 複数のメンバ関数が協力してオブジェクトを動かす仕組みを体験する
💬 クラス外部定義とは?
クラスを定義するとき、関数の宣言だけ を書いて、 中身(実装)はクラスの外で書くことができます。
class Player { public: void move(); // 宣言だけ(中身は後で書く) }; // クラスの外で実装 void Player::move() { // 動作の中身 }
Player::move() のように ::(スコープ解決演算子)を使って
「この関数はPlayerクラスのものですよ」と示します。
これを使うと、.hファイルと.cppファイルを分けて書く設計にもスムーズに移行できます。
💬 短い関数は「インライン定義」でもOK
短い関数ならクラスの中に直接書いてもよいですが、 長くなる処理や整理したいコードは外部定義のほうが見やすくなります。
| 方法 | 特徴 |
|---|---|
| クラスの中で書く(インライン定義) | 短い処理に向く。読みやすい。 |
| クラスの外で書く(外部定義) | 長い処理やファイル分割に向く。 |
🧮 例題:Playerクラス(外部定義)
目的: Vector2D を使い、クラス外部定義で動くキャラクターを作る。
#include <iostream> using namespace std; // ====================== // Vector2D クラス // ====================== class Vector2D { public: double x; double y; // 宣言 Vector2D(); Vector2D(double a, double b); Vector2D add(const Vector2D& v); }; // ---- Vector2D メンバ関数の外部定義 ---- //引数なしコンストラクタ Vector2D::Vector2D() { x = 0.0;//なにもしていしなかったら y = 0.0;//(0,0)で初期化 } //引数なしコンストラクタ(a,b)で初期化 Vector2D::Vector2D(double a, double b) { x = a; y = b; } Vector2D Vector2D::add(const Vector2D& v) { return Vector2D(x + v.x, y + v.y); } // ====================== // Player クラス // ====================== class Player { public: string name; Vector2D pos; // 位置ベクトル Player(string n, Vector2D p); void move(Vector2D v); void show(); }; // ---- Player メンバ関数の外部定義 ---- Player::Player(string n, Vector2D p) { name = n; pos = p; } void Player::move(Vector2D v) { pos = pos.add(v); } void Player::show() { cout << name << " の位置: (" << pos.x << ", " << pos.y << ")" << endl; } // ====================== // main 関数 // ====================== int main() { // 初期位置を明示的に指定 Player p("Hero", Vector2D(0, 0)); p.show(); p.move(Vector2D(1, 0)); // 右へ p.move(Vector2D(0, 1)); // 上へ p.move(Vector2D(1, 1)); // 右上へ p.show(); }
実行結果
Hero の位置: (0, 0) Hero の位置: (2, 2)
ポイント
- コンストラクタをオーバーロードしている(引数なし、あり)
- クラス定義では関数名と引数リストだけ書く。
- 関数本体は外部で定義し、
ClassName::関数名の形でつなぐ。 - この構造は、ヘッダ(.h)とソース(.cpp)分割の基礎になる。
🎮 イメージ図
Player ├─ name = "Hero" ├─ pos = (0,0) ├─ move(v) → pos += v └─ show() → 現在位置を表示
move() を呼ぶたびに pos が変化 → キャラの移動 になる!
🧩 練習問題(第5問)
Playerクラスを強化して、速度(velocity)と更新処理(update)を外部定義で追加せよ。
// クラス名: Player // - メンバ変数: name, pos, velocity(Vector2D型) // - コンストラクタ: 名前と初期位置を設定 // - メンバ関数: // 1. setVelocity(Vector2D v) … 速度ベクトルを設定 // 2. update() … 位置に速度を加算して移動 // 3. show() … 現在位置を表示 // // すべてクラス外部定義で記述すること。 // main()で以下を実行: // ・p1 = Player("Hero", Vector2D(0,0)) // ・速度を Vector2D(1,1) に設定 // ・3回 update() を呼び出して位置を表示 // // 出力例: // Step 0: (0, 0) // Step 1: (1, 1) // Step 2: (2, 2) // Step 3: (3, 3)
ヒント
- 外部定義では
Player::関数名の形を忘れずに。 - 関数が多くなるほど、このスタイルのほうが整理しやすくなる。
- update() をループで呼び出すと、フレームごとの移動表現ができる。
🎯 まとめ
| 覚えるポイント | 内容 |
|---|---|
| クラス外部定義 | 関数をクラスの外で定義し、ClassName::関数名 でつなぐ |
| スコープ解決演算子(::) | 所属先を示す。「この関数はPlayerクラスのもの」と教える記号 |
| コード整理 | ヘッダ(.h)とソース(.cpp)に分けやすくなる |
| ゲームとの関係 | move()で座標が変わる=キャラが動く! |
💡 補足:const参照(const &)とは?
C++では、関数にオブジェクトを渡すときに「コピー」が発生します。 しかし、Vector2D のように中身が大きいデータを毎回コピーすると、 メモリのムダや処理の遅さにつながります。
そのため、次のように書きます:
Vector2D add(const Vector2D& v);
ここで出てくる const と & には次のような意味があります。
| 記号 | 役割 |
|---|---|
| &(参照渡し) | コピーせずに「元のデータを参照」する。高速。 |
| const | 「読み取り専用」にする。関数の中で書き換え禁止。 |
つまり、const Vector2D& は
「Vector2D型のデータをコピーせず、安全に読み取る」
という意味になります。
たとえば、次の2つを比べると違いがわかります。
// (コピー渡し)データをまるごとコピー Vector2D add(Vector2D v); // (参照渡し)コピーしない Vector2D add(const Vector2D& v);
& を付けることで速度が上がり、const を付けることで安全になります。
ゲームプログラムでは多くのオブジェクトを扱うため、
この「const参照」を覚えておくと効率的な設計ができます。
📘 次回のステップ → 第6章では「Enemyクラス」を作り、Playerとのあたり判定(距離計算)を学びます。