Menu
CoddyTech

纯虚函数

本课属于 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 中是纯虚函数。如果派生类没有实现所有纯虚函数,它也会变成抽象类,无法实例化。

当基类无法提供有意义的默认实现时,纯虚函数就很重要。每种形状都有面积,但如果不知道具体的形状类型,就无法合理地计算“通用形状面积”。

challenge icon

挑战

简单

让我们构建一个支付处理系统,演示纯虚函数如何为不同的支付方式强制执行契约。你将创建一个抽象基类,定义每个支付处理器必须执行的操作,然后实现满足该契约的具体支付类型。

你将把代码组织在三个文件中:

  • 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 项输入(每项占一行):
    1. 信用卡账户 ID
    2. 信用卡手续费率(double)
    3. 银行账户 ID
    4. 银行名称

    创建两个支付处理器,并将它们存储在由 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;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器