C++ 継承・隠蔽・動的生成・オーバーライド 授業資料


  • 共通基底クラス CharaBase
  • 旧プレイヤークラス Player
  • 敵キャラクラス Enemy
  • 新しいプレイヤー NewPlayerCharaBase を継承)

メインループより:

"theMain.cpp (抜粋)"
NewPlayer nPlayer("NewHero", Vector2D(50,250), Vector2D(1.0f,0), 1.0f);
Enemy enemy("Monster", Vector2D(300,250), Vector2D(-5.0f,0), 1.0f);
 
void Update()
{
    nPlayer.Update();
    enemy.Update();
}
 
void Draw()
{
    nPlayer.Draw();
    enemy.Draw();
}

キャラに共通する情報:

  • name_:名前
  • pos_:位置
  • speed_:速度
  • radius_:半径
  • Update():移動処理
  • Draw():描画処理

これらを CharaBase にまとめてある。

"CharaBase.h (抜粋)"
class CharaBase
{
public:
    CharaBase(string name, Vector2D pos, Vector2D speed, float radius);
    void Update();
    void Draw();
 
    void SetName(string name);
    void SetPosition(Vector2D pos);
    string GetName() const;
    Vector2D GetPosition() const;
    void SetSpeed(Vector2D speed);
    Vector2D GetSpeed() const;
    void SetRadius(float radius);
    double GetRadius() const;
    void SetChara(string n, Vector2D p, Vector2D v, double r);
 
private:
    string   name_;
    Vector2D pos_;
    Vector2D speed_;
    float    radius_;
};
"CharaBase.cpp (Update 部分)"
void CharaBase::Update()
{
    float dt = GetDeltaTime();
    pos_.x = pos_.x + speed_.x * dt;
    pos_.y = pos_.y + speed_.y * dt;
}

"NewPlayer.h"
class NewPlayer : public CharaBase
{
public:
    NewPlayer(string name, Vector2D pos, Vector2D speed, float radius);
    void SayHello();
    bool GetIsSayHello() { return isSayHello_; }
 
private:
    bool isSayHello_;
};
"NewPlayer.cpp"
NewPlayer::NewPlayer(string name, Vector2D pos, Vector2D speed, float radius)
    : CharaBase(name, pos, speed, radius)
    , isSayHello_(false)
{
}
 
void NewPlayer::SayHello()
{
    isSayHello_ = true;
}

ポイント:

  • NewPlayer のコンストラクタで CharaBase を初期化
  • 共通部分は CharaBase が担当
  • NewPlayer 独自の isSayHello_ のみ自分で管理

継承では、基底クラスと同じ名前の関数を派生クラス側にも書くと、 「基底クラスの同名の関数が見えなくなる」ことがある。 この現象を 関数の隠蔽(いんぺい) という。


"BaseAndDerived.cpp"
class Base
{
public:
    void Update();
};
 
class Derived : public Base
{
public:
    // ここに Update() を書いたらどうなる?
    // void Update();
};

DerivedUpdate() を追加してみる。

"DerivedUpdate.cpp"
class Derived : public Base
{
public:
    void Update();   // Base::Update() と同じ名前
};

"CallUpdate.cpp"
Derived d;
d.Update();   // さて、どっちの Update() が呼ばれる?

→ 呼ばれるのは Derived::Update() (基底クラス Base の Update() は呼ばれない)


同じ名前の関数が派生クラスに書かれたことで、 基底クラスの Update() が名前の上で見えなくなる

つまり:

  • Derived に関数を書く
  • Base の同名関数は「隠れる」
  • → 呼ばれるのは派生クラス側の関数になる

この状態を 隠蔽(いんぺい) と呼ぶ。


最初の NewPlayer には Update() が実装されていない。

"NewPlayer_NoUpdate.cpp"
NewPlayer p("Hero", ...);
p.Update();   // CharaBase::Update() が呼ばれる

ここでは、NewPlayerUpdate() が無いので、 基底クラス CharaBase のものがそのまま使われる。


"NewPlayer_Hide.cpp"
class NewPlayer : public CharaBase
{
public:
    void Update();  // ← 自分で実装
};

これを呼ぶと:

"CallNewPlayer.cpp"
NewPlayer p("Hero", ...);
p.Update();   // NewPlayer::Update() が呼ばれる

CharaBase::Update() は呼ばれない。 → つまり、CharaBase::Update()隠蔽 されている。


  • ① NewPlayer に Update() を書く
  • ② 基底クラスの Update() と同名になる
  • ③ 呼び出すと継承先(NewPlayer)のほうが優先される
  • ④ 基底クラスの同名関数は見えなくなる(=隠蔽)

隠蔽は、 「派生クラスに書いた同名関数が、基底クラスの同名関数を上書きしたように見せる仕組み」 であり、派生クラス型の変数で使うと”そう見える” という現象である。

  • game-engineer/classes/2025/game-development-1/no-011.txt
  • 最終更新: 3カ月前
  • by root