Menu
Coddy logo textTech

抽象クラス

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 60/104。

abstract class とは、少なくとも 1 つの純粋仮想 function を含む class です。不完全なインターフェースを持つため、abstract class のオブジェクトを直接作成することは cannot であり、継承されるためだけに存在します。

class Shape {
public:
    virtual double area() = 0;      // 純粋仮想 - Shapeを抽象にする
    virtual double perimeter() = 0; // 別の純粋仮想関数
    
    void printInfo() {              // 通常のメソッド - 実装がある
        std::cout << "Area: " << area() << std::endl;
    }
    
    virtual ~Shape() = default;
};

Shape s;  // エラー: 抽象クラスをインスタンス化できない

抽象クラスには、純粋仮想関数(実装なし)、通常の仮想関数(デフォルト実装あり)、および非仮想関数を組み合わせて含めることができます。これにより、共通の動作を定義しながら、派生クラスに特定の機能の実装を要求できます。

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};

Circle c(5.0);      // OK: Circle はすべての純粋仮想関数を実装しています
c.printInfo();      // 継承された printInfo() を使用し、Circle の area() を呼び出します

派生クラスが具象(インスタンス化可能)になるには、すべての純粋仮想関数を実装する必要があります。未実装のものを残すと、そのクラス自体が abstract のままになります。この仕組みにより、階層内のすべての具象クラスが必要な機能を提供することが保証されます。

challenge icon

チャレンジ

簡単

異なるメディアタイプに対して abstract クラスがどのように契約を定義するかを示すメディアプレーヤーシステムを構築しましょう。すべてのメディアプレーヤーが実行すべき内容を定める abstract な基底クラスを作成し、その後、音声および動画コンテンツ用の具象プレーヤーを実装します。

コードを3つのファイルに分けて整理します。

  • MediaPlayer.h:すべてのメディアタイプの設計図となる abstract な MediaPlayer クラスを定義します。abstract クラスには、次の要素を含めます。
    • protected な std::string title メンバー
    • protected な int duration メンバー(秒単位)
    • 両方の値を初期化する constructor
    • pure virtual の play() メソッド:各メディアタイプは異なる方法で再生します
    • std::string を返す pure virtual の getMediaType() メソッド
    • <title> (<duration>s) - Type: <mediaType> を出力する通常の(virtual ではない)showInfo() メソッド。このメソッドでは、タイプを取得するために getMediaType() を呼び出します
    • virtual destructor
  • Players.hMediaPlayer を継承する2つの具象メディアプレーヤーを実装します。

    AudioPlayer

    • private な std::string artist メンバー
    • title、duration、artist を受け取る constructor
    • play() を実装し、Playing audio: <title> by <artist> を出力します
    • getMediaType() を実装し、"Audio" を返します

    VideoPlayer

    • private な std::string resolution メンバー
    • title、duration、resolution を受け取る constructor
    • play() を実装し、Playing video: <title> in <resolution> を出力します
    • getMediaType() を実装し、"Video" を返します
  • main.cpp:4つの入力を読み取ります(それぞれ別の行に入力されます)。
    1. 音声トラックのタイトル
    2. 音声の duration(整数)
    3. 動画のタイトル
    4. 動画の duration(整数)

    artist に "Unknown Artist" を指定した AudioPlayer と、resolution に "1080p" を指定した VideoPlayer を作成します。両方を MediaPlayer* ポインターの配列に格納します。

    配列をループし、各プレーヤーについて、まず showInfo() を呼び出し、その後 play() を呼び出します。各メディア項目の間には空行を出力します。処理が完了したら、dynamically allocated されたオブジェクトを Clean up します。

たとえば、入力が Summer Vibes180Nature Documentary3600 の場合:

Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist

Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080p

showInfo() メソッドは abstract な基底クラスで一度だけ定義されていますが、pure virtual の getMediaType() を呼び出している点に注目してください。これは、abstract クラスが具象的な実装と、Derived クラスが提供しなければならないメソッドを組み合わせられることを示しています。オーバーライドするすべてのメソッドで override キーワードを使用することを忘れないでください。

自分で試してみよう

#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"

int main() {
    // 入力を読み取る
    std::string audioTitle;
    int audioDuration;
    std::string videoTitle;
    int videoDuration;
    
    std::getline(std::cin, audioTitle);
    std::cin >> audioDuration;
    std::cin.ignore();
    std::getline(std::cin, videoTitle);
    std::cin >> videoDuration;
    
    // TODO: アーティスト "Unknown Artist" の AudioPlayer を作成する
    
    // TODO: 解像度 "1080p" の VideoPlayer を作成する
    
    // TODO: 両方を MediaPlayer* ポインタの配列に格納する
    
    // TODO: 配列をループして各プレイヤーについて:
    //       1. showInfo() を呼び出す
    //       2. play() を呼び出す
    //       3. 各メディアアイテムの間に空行を出力する
    
    // TODO: 動的に割り当てられたオブジェクトをクリーンアップする
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ