ポリモーフィズム: 多態性

1つのインターフェース、多様な実装。OOPの核心。

ポリモーフィズム
同じインターフェースで異なる実装を扱うこと。
仮想関数
実行時に呼び出し先が決まる(動的束縛)関数。
CRTP
静的ポリモーフィズム。テンプレートを使ってコンパイル時に解決。

ポリモーフィズムとは?

万能リモコン (Universal Remote)

ポリモーフィズムは「万能リモコン」です。このリモコンには「再生」ボタンしかありません。しかし、テレビに向ければ「テレビ番組」が映り、オーディオに向けば「音楽」が流れます。ボタン(インターフェース)は一つですが、相手(オブジェクト)によって振る舞いが変わります。

C++では `virtual` 関数を使ってこれを実現します。基底クラスのポインタや参照を通して、実際には異なる派生クラスの関数を呼び出せます。利用する側は「Shape(図形)である」ことだけ知っていればよく、「それが円か四角か」を知る必要がなくなります。

Virtual Functions
class Shape {
public:
// ポリモーフィズムを使うなら仮想デストラクタ必須
virtual ~Shape() = default;
// 純粋仮想関数(派生クラスで実装必須)
virtual double area() const = 0;
// 仮想関数(デフォルト実装あり)
virtual void draw() const {
std::cout << "Drawing shape
";
}
};
class Circle : public Shape {
double radius_;
public:
Circle(double r) : radius_(r) {}
// override キーワードでコンパイラにチェックさせる
double area() const override {
return 3.14159 * radius_ * radius_;
}
};
// ポリモーフィズムの使用
void printArea(const Shape& shape) {
// 実行時の型(Circleなど)に応じて適切な area() が呼ばれる
std::cout << shape.area() << "
";
}
実行結果
Shape* s = new Circle(5);
s->area();  // 78.5 (Circleのareaが呼ばれる)
Bad
// ❌ Bad: 型による分岐(手動)
void draw(const Shape& s) {
// dynamic_cast は遅いし、型が増えるたびに修正が必要
if (auto* c = dynamic_cast<const Circle*>(&s)) {
drawCircle(*c);
} else if (auto* r = dynamic_cast<const Rect*>(&s)) {
drawRect(*r);
}
}
Good
// ✅ Good: 仮想関数(自動)
void draw(const Shape& s) {
// オブジェクト自身が自分の描き方を知っている
s.draw();
}

高度なパターン

CRTP & std::variant
// CRTP(Curiously Recurring Template Pattern)
// 静的ポリモーフィズム:仮想関数なしで多態性を実現(高速)
template<typename Derived>
class Base {
public:
void interface() {
// コンパイル時に型が決まる
static_cast<Derived*>(this)->implementation();
}
};
class Concrete : public Base<Concrete> {
public:
void implementation() { /* ... */ }
};
// std::variant(C++17): 型安全な Union
// 継承関係がなくても多態性を実現できる
using ShapeType = std::variant<Circle, Rectangle, Triangle>;
std::vector<ShapeType> shapes;
for (auto& s : shapes) {
std::visit([](auto& arg){ arg.draw(); }, s);
}
Tip: 仮想関数のオーバーヘッド(vtable参照)がシビアな場合、CRTP を検討します。

合格ライン

純粋仮想関数を定義して抽象クラスを作れる
override を使って意図を明確にできる

参考リンク

演習課題

課題1: 図形クラス
Shape 抽象クラスと Circle, Rectangle 派生クラスを作成し、area() を多態的に呼び出してください。
課題2: virtual デストラクタ
virtual デストラクタを持つ基底クラスを作成し、派生クラスを delete したときに適切に解放されることを確認してください。

次のステップ