【C++】抽象クラスと純粋仮想関数とは?継承先に実装を強制する方法

目次

はじめに

C++の継承において、親クラス(基底クラス)の段階では具体的な処理内容は決められないが、「このクラスを継承する子クラス(派生クラス)は、必ずこの機能(メンバ関数)を実装しなければならない」というルールを設けたい場合があります。

例えば、「図形」という親クラスには「面積を計算する (getArea)」という機能が必要ですが、「図形」というだけでは具体的な計算方法は分かりません。「円」や「四角形」といった子クラスになって初めて、具体的な計算式が決まります。

このような「機能の枠組みだけを親クラスで定義し、具体的な実装を子クラスに強制する」仕組みが、純粋仮想関数抽象クラスです。


抽象クラスと純粋仮想関数のサンプルコード

このコードは、Character(キャラクター)という抽象クラスを定義します。attack という純粋仮想関数を持つことで、「キャラクターは攻撃手段を持つべきだ」というルールを定めます。

完成コード

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// --- 1. 抽象クラス(基底クラス) ---
class Character {
protected:
    string name;
public:
    Character(string n) : name(n) {}
    
    // 2. 純粋仮想関数 "= 0" を付けて、実装を持たないことを示す
    virtual void attack() = 0;
};

// --- 3. 派生クラス1 ---
class Warrior : public Character {
public:
    Warrior(string n) : Character(n) {}
    
    // 4. 純粋仮想関数をオーバーライドして、具体的な処理を実装
    void attack() {
        cout << name << " は剣で攻撃した!" << endl;
    }
};

// --- 3. 派生クラス2 ---
class Mage : public Character {
public:
    Mage(string n) : Character(n) {}
    
    // 4. 純粋仮想関数をオーバーライドして、具体的な処理を実装
    void attack() {
        cout << name << " は魔法を唱えた!" << endl;
    }
};

int main() {
    // NG: 抽象クラスのオブジェクトは作成できない!
    // Character genericChar("ただのキャラ"); // コンパイルエラー!
    
    // 基底クラスへのポインタを使って、派生クラスのオブジェクトを扱う
    vector<Character*> party;
    party.push_back(new Warrior("戦士"));
    party.push_back(new Mage("魔法使い"));

    for (Character* p_char : party) {
        p_char->attack(); // ポリモーフィズムが機能し、それぞれのattackが呼ばれる
    }

    // メモリの解放
    for (Character* p_char : party) {
        delete p_char;
    }
    
    return 0;
}

コードの解説

1. 抽象クラス (Character)

一つ以上の純粋仮想関数を持つクラスは、自動的に抽象クラスになります。抽象クラスは、それ自身のオブジェクトを直接作成することができません(new Character() などはコンパイルエラー)。抽象クラスは、あくまで他のクラスに継承されるための「不完全な設計図」としての役割に特化します。

2. 純粋仮想関数 (virtual void attack() = 0;)

  • virtual: この関数が、子クラスでオーバーライドされることを示します。
  • = 0: この構文が、この関数が純粋仮想関数であることを示します。これは、「この関数には実装(処理内容)がありません。子クラスが必ず実装してください」という意味です。

3. 派生クラス (Warrior, Mage)

抽象クラス Character を継承した子クラスは、親クラスが持つ全ての純粋仮想関数を、必ずオーバーライドして実装しなければならないというルールが課せられます。もし Warrior クラスが attack 関数を実装しなかった場合、Warrior クラス自身も抽象クラスとなり、オブジェクトを作成できなくなります。

4. ポリモーフィズムの実現

main関数では、Character* という親クラスへのポインタを使って、WarriorMage という異なる型の子クラスオブジェクトを統一的に扱っています。

p_char->attack() を呼び出すと、attack が仮想関数であるため、ポリモーフィズム(多態性)が機能し、ポインタが実際に指しているオブジェクトの型(Warrior なのか Mage なのか)に応じて、それぞれの attack 関数が正しく呼び分けられます。


まとめ

今回は、C++の抽象クラスと純粋仮想関数について解説しました。

  • 純粋仮想関数 (virtual ... = 0;): 実装を持たず、子クラスに実装を強制するための関数。
  • 抽象クラス: 純粋仮想関数を一つでも持つクラス。オブジェクトを直接作成できない。継承されるための「設計図」の役割。

この仕組みは、オブジェクト指向において「インターフェースの統一」という非常に重要な役割を果たします。「『キャラクター』というカテゴリに属するものは、必ず『攻撃』できなければならない」といった、プログラム全体の設計ルールを厳密に定義し、一貫性を保つために不可欠な機能です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次