純粋仮想関数
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 59/104。
純粋仮想関数とは、基底クラスに実装を持たない仮想関数です。関数宣言に = 0 を割り当てることで宣言します。これは、派生クラスが独自の実装を必ず提供しなければならないことをコンパイラに伝えます。
class Shape {
public:
virtual double area() = 0; // 純粋仮想関数
virtual ~Shape() = default;
};デフォルトの動作を提供する通常の仮想関数とは異なり、純粋仮想関数は契約を定義します。具象派生クラスは、この関数を実装しなければインスタンス化できません。基底クラスは、単に何を行う必要があるかを宣言するだけであり、どのように行うかは定義しません。
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() override {
return width * height;
}
};Circle と Rectangle はどちらも Shape 内で純粋仮想関数となっているため、area() を実装する必要があります。派生クラスがすべての純粋仮想関数の実装に失敗すると、そのクラスも抽象クラスとなり、インスタンス化できません。
純粋仮想関数は、基底クラスが意味のあるデフォルト実装を提供できない場合に不可欠です。すべての図形には area がありますが、特定の図形型を知らずに「一般的な図形の area」を calculate する適切な方法はありません。
チャレンジ
簡単異なる支払い方法全体で純粋仮想関数がどのように契約を強制するかを示す、支払い処理システムを構築しましょう。すべての支払いプロセッサーが実行すべき内容を定義する抽象基底 class を作成し、その契約を満たす具象的な支払いタイプを実装します。
コードを3つのファイルに分けて整理します。
PaymentProcessor.h: すべての支払い方法の設計図となる抽象PaymentProcessorclass を定義します。この class には次の要素を含めます。- protected な
std::string accountIdメンバー - account ID を初期化する constructor
- 純粋仮想
processPayment(double amount)メソッド:すべての支払いタイプがそれぞれ異なる方法で実装する必要があります std::stringを返す純粋仮想getProcessorName()メソッド- virtual destructor
- protected な
PaymentMethods.h:PaymentProcessorを継承する2つの具象的な支払いプロセッサーを実装します。CreditCardProcessor:- private な
double feePercentageメンバー(取引手数料率) - account ID と手数料率を受け取る constructor
processPayment()を実装して手数料(amount * feePercentage / 100)を計算し、次を出力します。Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>)getProcessorName()を実装して"CreditCard"を返します
BankTransferProcessor:- private な
std::string bankNameメンバー - account ID と銀行名を受け取る constructor
processPayment()を実装して次を出力します。Bank Transfer [<accountId>] via <bankName>: Transferred $<amount>getProcessorName()を実装して"BankTransfer"を返します
- private な
main.cpp: 4つの入力を読み取ります(それぞれ別の行に入力します)。- Credit card の account ID
- Credit card の手数料率(double)
- Bank の account ID
- Bank の名前
両方の支払いプロセッサーを作成し、
PaymentProcessor*ポインターの array に格納します。各プロセッサーを通して100.0の支払いを処理し、各取引の前にプロセッサー名を表示します。Processing with <processorName>: <processPayment output>プロセッサーの間に空行を出力します。処理が終わったら、dynamically allocated されたオブジェクトを Clean up します。
たとえば、入力が CC-4521、2.5、BA-7890、National Bank の場合:
Processing with CreditCard:
Credit Card [CC-4521]: Charged $100 (Fee: $2.5)
Processing with BankTransfer:
Bank Transfer [BA-7890] via National Bank: Transferred $100PaymentProcessor には純粋仮想関数があるため、直接インスタンス化することはできません。作成できるのは、契約を満たす具象的な implementation のみです。これにより、すべての支払い方法がそれぞれ固有の処理ロジックを提供することが保証されます。
自分で試してみよう
#include <iostream>
#include <string>
#include "PaymentMethods.h"
using namespace std;
int main() {
// 入力を読み取る
string ccAccountId;
double feePercentage;
string bankAccountId;
string bankName;
getline(cin, ccAccountId);
cin >> feePercentage;
cin.ignore();
getline(cin, bankAccountId);
getline(cin, bankName);
// TODO: PaymentProcessor* ポインタの配列を2要素で作成する
// TODO: CreditCardProcessor と BankTransferProcessor のオブジェクトを作成する
// そしてそれらを配列に格納する
// TODO: 配列をループして各プロセッサについて:
// 1. Print "Processing with <processorName>:"
// 2. amount 100.0 で processPayment を呼び出す
// 3. プロセッサ間に空行を出力する(最後の後には出力しない)
// TODO: 動的に割り当てられたオブジェクトをクリーンアップする
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ