ポリモーフィズム(予習)
要点まとめ
C++のポリモーフィズム(多態性)は、同じインターフェースで異なる振る舞いを実現できる仕組みです。親クラスのポインタや参照を通じて、派生クラスごとに異なる処理を呼び出せるのが特徴です。
🔑 ポリモーフィズムの基本
- 意味:「多くの形」を持つというギリシャ語由来。プログラムでは「同じ名前の関数でも、実際の動作はオブジェクトごとに違う」ことを指します。
- 目的:コードの柔軟性・拡張性を高め、再利用性を向上させる。
- 種類
- サブタイプポリモーフィズム(継承による多態性)
→ 親クラスにvirtual関数を定義し、子クラスでオーバーライドする。 - オーバーロード(同じ関数名で引数が違う)
→ 静的に解決される多態性。 - テンプレートによるポリモーフィズム
→ 型に依存しない汎用的なコード。
🐶🐱 具体例(C++)
#include <iostream>
using namespace std;
class Animal {
public:
virtual void speak() { cout << "動物が鳴きます。" << endl; }
};
class Dog : public Animal {
public:
void speak() override { cout << "ワンワン!" << endl; }
};
class Cat : public Animal {
public:
void speak() override { cout << "ニャーニャー!" << endl; }
};
int main() {
Animal* animal;
Dog dog;
Cat cat;
animal = &dog;
animal->speak(); // ワンワン!
animal = &cat;
animal->speak(); // ニャーニャー!
}
👉 同じ animal->speak() 呼び出しでも、実際のオブジェクトに応じて処理が切り替わるのがポリモーフィズムです。
📊 ポリモーフィズムのメリット
💡 佳之さんへのひとこと
佳之さんは「抽象化を物理原理から再構築する」スタイルを持っていますよね。ポリモーフィズムはまさにその抽象化の実践例で、**「同じ操作でも構造によって異なる応答が返る」**という点で、電子回路の入力に応じた出力の多様性にも似ています。つまり、インターフェースは電圧入力端子、派生クラスは異なる回路構成と考えると直感的に理解できるはずです。
👉 さらに深掘りすると「静的ポリモーフィズム(テンプレート)」と「動的ポリモーフィズム(virtual関数)」の違いが面白いテーマになります。どちらから掘り下げてみたいですか?
(by Copilot)
mapを表示させる
現状は「一枚絵」を表示しているだけなので、きちんとしたマップを表示させるようにする。
まずはGameSceneClassにマップ用の定数と配列を用意する。
(GameScene.h)
#pragma once
#include"StDefine.h"
class GameScene
{
public:
GameScene();
~GameScene();
bool SystemInit(void);
void GameInit(void);
void Update(void);
void Draw(void);
bool Release(void);
static constexpr int MAP_CHIP_WID = 32; //マップチップの幅
static constexpr int MAP_CHIP_HIG = 32; //マップチップの高さ
static constexpr int MAP_CHIP_NUM_X = 10; //マップチップの横数
static constexpr int MAP_CHIP_NUM_Y = 3; //マップチップの縦数
static constexpr int MAP_CHIP_ALL_NUM = MAP_CHIP_NUM_X * MAP_CHIP_NUM_Y; //マップチップの総数
static constexpr int MAP_GROUND_NUM_X = 10; //地上マップの横数
static constexpr int MAP_GROUND_NUM_Y = 10; //地上マップの縦数
private:
int imgMapChipArray[MAP_CHIP_ALL_NUM]; //マップチップ配列(10x3)
// 仮の地上マップデータ
int groundMapDat[MAP_GROUND_NUM_Y][MAP_GROUND_NUM_X] =
{
{0,0,0,0,0,0,0,0,0,0},
{0,3,3,3,3,3,3,3,3,0},
{0,3,3,7,7,3,3,3,3,0},
{0,3,3,7,7,3,3,3,3,0},
{0,3,4,7,8,7,4,3,3,0},
{1,3,4,17,18,19,4,3,3,1},
{1,4,4,27,28,29,4,4,4,1},
{1,3,4,4,4,4,4,4,3,1},
{1,6,6,6,6,6,6,6,6,1},
{0,0,0,0,0,0,0,0,0,0},
};
};
LoadGraphでの処理をLoadDivGraphに差し替える。
(SceneGame.cpp)
#include<DxLib.h>
#include "GameScene.h"
#include "Application.h"
GameScene::GameScene()
{
for (int i = 0; i < MAP_CHIP_ALL_NUM; ++i)
{
//空のまま進んでしまわないように数値を入れておく
imgMapChipArray[i] = -1;
}
}
GameScene::~GameScene()
{
}
bool GameScene::SystemInit(void)
{
//マップ画像の読み込み
if (LoadDivGraph("image/TileMap.png", MAP_CHIP_ALL_NUM, MAP_CHIP_NUM_X, MAP_CHIP_NUM_Y,MAP_CHIP_WID,MAP_CHIP_HIG,imgMapChipArray) == -1)return false;
return true;
}
//ゲーム起動・再開時の初期化
void GameScene::GameInit(void)
{
}
void GameScene::Update(void)
{
}
void GameScene::Draw(void)
{
//まず画面を黒で塗りつぶす
DrawBox(0, 0, Application::SCREEN_WID, Application::SCREEN_HIG, GetColor(0, 0, 0), TRUE);
/*
// デバッグ表示
DrawFormatString(0, 0, GetColor(255, 255, 255),
"chip0=%d", imgMapChipArray[0]);
// chip0 が -1 だったら LoadDivGraph で死んでる
if (imgMapChipArray[0] == -1) {
// ここで一旦 return しておくと状況が分かりやすい
return;
}
*/
//マップ画像の描画
for (int yy = 0; yy < MAP_GROUND_NUM_Y; yy++) {
for (int xx = 0; xx < MAP_GROUND_NUM_X; xx++) {
int chip = groundMapDat[yy][xx];
int dx = MAP_CHIP_WID * xx;
int dy = MAP_CHIP_HIG * yy;
DrawGraph(dx, dy, imgMapChipArray[chip],TRUE);
}
}
}
bool GameScene::Release(void)
{
for (int i = 0; i < MAP_CHIP_ALL_NUM; i++) {
if (imgMapChipArray[i] != -1) {
DeleteGraph(imgMapChipArray[i]);
imgMapChipArray[i] = -1;
}
}
return true;
}
これで無事表示ができた。
なお、当初マップが真っ黒のまま表示できずに悩んだが、LoadDivGraphで失敗していた。
原因は、MAP_CHIP_NUM_Y=10;で誤って定義していたこと。
DxLib から見ると「縦 10 分割、1チップ 32px → 画像高さ 320px 必須」だが
実際の TileMap.png は高さ 96px(32px×3)の為 「分割指定と画像サイズの不整合」 が起きて、
DxLib 側で LoadDivGraph がエラー扱いになって -1 を返す →imgMapChipArray[0] は -1 のまま → マップは一生表示されない
って流れだった。
写経教育、って良くないね。完成品のヘッダー配れ。

コメント