纯虚函数
本课属于 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 都必须实现 area(),因为它在 Shape 中是纯虚函数。如果派生类没有实现所有纯虚函数,它也会变成抽象类,无法实例化。
当基类无法提供有意义的默认实现时,纯虚函数就很重要。每种形状都有面积,但如果不知道具体的形状类型,就无法合理地计算“通用形状面积”。
挑战
简单让我们构建一个支付处理系统,演示纯虚函数如何为不同的支付方式强制执行契约。你将创建一个抽象基类,定义每个支付处理器必须执行的操作,然后实现满足该契约的具体支付类型。
你将把代码组织在三个文件中:
PaymentProcessor.h:定义一个抽象的PaymentProcessor类,作为所有支付方式的蓝图。这个类应包含:- 一个受保护的
std::string accountId成员 - 一个用于初始化账户 ID 的构造函数
- 一个纯虚
processPayment(double amount)方法:每种支付类型都必须以不同方式实现它 - 一个纯虚
getProcessorName()方法,返回std::string - 一个虚析构函数
- 一个受保护的
PaymentMethods.h:实现两个继承自PaymentProcessor的具体支付处理器:CreditCardProcessor:- 一个私有的
double feePercentage成员(交易手续费率) - 一个接收账户 ID 和手续费率的构造函数
- 实现
processPayment()来计算手续费(amount * feePercentage / 100),然后输出:Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>) - 实现
getProcessorName(),使其返回"CreditCard"
BankTransferProcessor:- 一个私有的
std::string bankName成员 - 一个接收账户 ID 和银行名称的构造函数
- 实现
processPayment(),使其输出:Bank Transfer [<accountId>] via <bankName>: Transferred $<amount> - 实现
getProcessorName(),使其返回"BankTransfer"
- 一个私有的
main.cpp:读取 4 项输入(每项占一行):- 信用卡账户 ID
- 信用卡手续费率(double)
- 银行账户 ID
- 银行名称
创建两个支付处理器,并将它们存储在由
PaymentProcessor*指针组成的数组中。通过每个处理器处理一笔金额为100.0的支付,并在每笔交易前显示处理器名称:Processing with <processorName>: <processPayment output>在两个处理器之间输出一个空行。完成后,清理动态分配的对象。
例如,输入 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 $100由于 PaymentProcessor 含有纯虚函数,因此你不能直接实例化它:只有满足该契约的具体实现才能被创建。这样可以确保每种支付方式都提供自己的特定处理逻辑。
亲手试试
#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: 创建一个包含 2 个 PaymentProcessor* 指针的数组
// TODO: 创建 CreditCardProcessor 和 BankTransferProcessor 对象
// 并将它们存入数组
// TODO: 遍历数组,对每个处理器执行以下操作:
// 1. 输出 "Processing with <processorName>:"
// 2. 以 100.0 为金额调用 processPayment
// 3. 在处理器之间输出一个空行(最后一个处理器之后不输出)
// TODO: 清理动态分配的对象
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器