ポリモーフィズム: 多態性
1つのインターフェース、多様な実装。OOPの核心。
ポリモーフィズム
同じインターフェースで異なる実装を扱うこと。
仮想関数
実行時に呼び出し先が決まる(動的束縛)関数。
CRTP
静的ポリモーフィズム。テンプレートを使ってコンパイル時に解決。
ポリモーフィズムとは?
C++では `virtual` 関数を使ってこれを実現します。基底クラスのポインタや参照を通して、実際には異なる派生クラスの関数を呼び出せます。利用する側は「Shape(図形)である」ことだけ知っていればよく、「それが円か四角か」を知る必要がなくなります。
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(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 したときに適切に解放されることを確認してください。