フリーキーズ | 独学プログラミング

C++の純粋仮想関数と抽象クラスでインターフェース設計を極める

リンドくん

リンドくん

たなべ先生、C++の純粋仮想関数って何ですか?なんだか難しそうですね...

たなべ

たなべ

確かに最初は難しそうに見えるよね。でも実は、良いプログラム設計をするための重要なツールなんだ。
例えば、ゲームの敵キャラクターの設計を考えてみよう。全部の敵が同じ行動をするわけじゃないよね?

リンドくん

リンドくん

なるほど!スライムとドラゴンでは全然違う行動をしますもんね。

たなべ

たなべ

その通り!そういう「共通の枠組みは決めておきたいけど、具体的な実装は後で決めたい」というときに使うのが純粋仮想関数なんだ。

プログラミングを学んでいる皆さんは、C++の学習を進めていく中で「純粋仮想関数」や「抽象クラス」という言葉を聞いたことがあるのではないでしょうか?

これらの概念は、優れたプログラム設計の基礎となる重要な機能ですが、初心者の方にとってはやや理解しにくい部分でもあります。

本記事では、C++の純粋仮想関数と抽象クラスについて、初心者の方でも理解しやすいよう、具体的なサンプルコードを交えながら丁寧に解説していきます。

プログラミング学習でお悩みの方へ

HackATAは、エンジニアを目指す方のためのプログラミング学習コーチングサービスです。 経験豊富な現役エンジニアがあなたの学習をサポートします。

✓ 質問し放題

✓ β版公開中(2025年内の特別割引)

HackATAの詳細を見る

純粋仮想関数とは?基本概念を理解しよう

リンドくん

リンドくん

純粋仮想関数って具体的にはどんなものなんですか?

たなべ

たなべ

純粋仮想関数は、「この関数は必ず作ってください」という約束のようなものなんだ。
実際のコードは書かずに、「こんな関数が必要です」という宣言だけをするんだよ。

純粋仮想関数の基本構文

純粋仮想関数とは、基底クラス(親クラス)で宣言だけを行い、派生クラス(子クラス)で必ず実装しなければならない関数のことです。

C++では、関数の末尾に= 0をつけることで純粋仮想関数を宣言します。

class BaseClass
{
public:
    // 純粋仮想関数の宣言
    virtual void pureVirtualFunction() = 0;
    
    // 通常の仮想関数
    virtual void normalVirtualFunction()
    {
        std::cout << "基底クラスの実装" << std::endl;
    }
};

なぜ純粋仮想関数が必要なのか?

純粋仮想関数を使う理由は、統一されたインターフェースを提供しつつ、具体的な実装を派生クラスに委ねることができるからです。

例えば、動物を表現するクラスを考えてみましょう。すべての動物は「鳴く」という行動を取りますが、その鳴き方は動物によって異なります。

class Animal
{
public:
    // すべての動物は鳴くが、鳴き方は異なる
    virtual void makeSound() = 0;
    
    // 共通の行動
    void eat()
    {
        std::cout << "食事中..." << std::endl;
    }
};

このように、共通の枠組みを定義しながら、具体的な実装は各派生クラスで決められるのが純粋仮想関数の大きな利点です。

抽象クラスの仕組みとインスタンス化の制限

リンドくん

リンドくん

抽象クラスって何ですか?普通のクラスとどう違うんですか?

たなべ

たなべ

抽象クラスは、純粋仮想関数を1つ以上持つクラスのことなんだ。
重要なのは、抽象クラスは直接インスタンスを作れないということなんだよ。

抽象クラスの特徴

抽象クラスとは、1つ以上の純粋仮想関数を持つクラスのことです。
抽象クラスには以下のような特徴があります。

  • インスタンス化できない - 直接オブジェクトを作成することはできません
  • 派生クラスでの実装を強制 - 純粋仮想関数を実装しないと派生クラスもインスタンス化できません
  • 共通の属性やメソッドを定義可能 - 純粋仮想関数以外の通常のメンバも持てます

実際のコード例で確認

#include <iostream>
#include <string>

// 抽象クラス
class Shape
{
protected:
    std::string name;
    
public:
    Shape(const std::string& n) : name(n) {}
    
    // 純粋仮想関数
    virtual double getArea() = 0;
    virtual void draw() = 0;
    
    // 通常のメンバ関数
    void displayName()
    {
        std::cout << "図形の名前: " << name << std::endl;
    }
};

// 具象クラス(派生クラス)
class Circle : public Shape
{
private:
    double radius;
    
public:
    Circle(double r) : Shape("円"), radius(r) {}
    
    // 純粋仮想関数の実装
    double getArea() override
    {
        return 3.14159 * radius * radius;
    }
    
    void draw() override
    {
        std::cout << "円を描画しました" << std::endl;
    }
};

int main()
{
    // Shape shape;  // エラー!抽象クラスはインスタンス化できない
    
    Circle circle(5.0);  // OK!具象クラスはインスタンス化可能
    circle.displayName();
    std::cout << "面積: " << circle.getArea() << std::endl;
    circle.draw();
    
    return 0;
}

このコードでは、Shapeクラスが抽象クラスとして定義されており、Circleクラスがその純粋仮想関数を実装した具象クラスとなっています。

インターフェース設計のベストプラクティス

リンドくん

リンドくん

インターフェース設計で気をつけるべきポイントはありますか?

たなべ

たなべ

いい質問だね!インターフェース設計にはいくつかの重要な原則があるんだ。
特に、単一責任の原則インターフェース分離の原則は覚えておくと良いよ。

インターフェース設計の基本原則

優れたインターフェース設計を行うためには、以下の原則を意識することが大切です。

1. 単一責任の原則(Single Responsibility Principle)

  • 1つのクラスは1つの責任のみを持つべきです

2. インターフェース分離の原則(Interface Segregation Principle)

  • 使用しないメソッドに依存させるべきではありません

3. 依存関係逆転の原則(Dependency Inversion Principle)

  • 抽象に依存し、具象に依存しないようにします

まとめ

リンドくん

リンドくん

純粋仮想関数と抽象クラスの使い方がよくわかりました!

たなべ

たなべ

素晴らしい!この技術を使えば、拡張性の高いプログラムを作れるようになるよ。
新しい機能を追加するときも、既存のコードを変更せずに済むことが多いんだ。これがプロの技術の基本になるんだよ。

C++の純粋仮想関数と抽象クラスは、優れたプログラム設計の基盤となる重要な概念です。

重要なポイントを振り返りましょう。

  • 純粋仮想関数は、派生クラスで必ず実装しなければならない関数です
  • 抽象クラスは、純粋仮想関数を持つクラスで、直接インスタンス化できません
  • インターフェース設計により、共通の枠組みを定義しながら具体的な実装を分離できます
  • ポリモーフィズムと組み合わせることで、柔軟で拡張性の高いシステムを構築できます

これらの技術を習得することで、以下のようなメリットが得られます。

  • コードの再利用性が向上します
  • 保守性の高いプログラムが書けるようになります
  • 新機能の追加が容易になります
  • チーム開発での協業がスムーズになります

プログラミングを学んでいる皆さんにとって、これらの概念は最初は難しく感じるかもしれません。
しかし、実際にコードを書いて動かしてみることで、その威力を実感できるはずです。

プログラミングは実践が何より大切です。今日学んだ概念を活用して、素晴らしいソフトウェアを作り上げてください。

この記事をシェア

関連するコンテンツ