跳转至

练习:使用虚析构函数管理复读机对象


任务描述

设计基类 Repeater、正向复读机类 ForRepeater 和反向复读机类 RevRepeater,并实现工厂函数 CreateRepeater。程序通过 Repeater* 调用不同派生类的 Play,再通过同一基类指针删除对象。

相关知识

基类指针与虚函数

基类指针可指向派生类对象。若 Play 是虚函数,通过 Repeater* 调用时会根据对象实际类型选择最后覆盖的版本:

Repeater* repeater = new ForRepeater;
repeater->Play();  // 调用 ForRepeater::Play

虚析构函数

基类指针用于销毁派生对象时,基类析构函数必须为虚函数:

virtual ~Repeater();

这样执行 delete repeater; 时,先调用派生类析构函数,再调用 Repeater 析构函数。若缺少 virtual,派生类的清理工作可能不会执行。

工厂函数

CreateRepeater 将创建逻辑集中起来,根据类型返回基类指针。调用方只依赖 Repeater 接口,不需要关心对象的具体派生类型。

编程要求

  1. 使用 C++11 标准编写单文件程序。
  2. 声明基类 Repeater
virtual void Play();
virtual ~Repeater();

Play 不输出内容;析构函数输出 砰!

  1. ForRepeater 公有继承 Repeater,覆盖 Play 输出 没想到你也是一个复读机,析构函数输出 正·复读机 炸了
  2. RevRepeater 公有继承 Repeater,覆盖 Play 输出 机读复个一是也你到想没,析构函数输出 机读复·反 炸了
  3. 完成普通函数:
Repeater* CreateRepeater(int type);

type == 0 时创建 ForRepeater;当 type == 1 时创建 RevRepeater;其他值返回 nullptr

  1. test 中创建两种复读机,捕获 Playdelete 的输出并断言;同时验证非法类型返回空指针。

待完成代码

#include <cassert>
#include <iostream>
#include <sstream>

class Repeater {
public:
    // TODO:声明虚函数 Play 和虚析构函数
};

class ForRepeater : public Repeater {
    // TODO:覆盖 Play,并实现析构函数
};

class RevRepeater : public Repeater {
    // TODO:覆盖 Play,并实现析构函数
};

// TODO:根据 type 动态创建对应对象
Repeater* CreateRepeater(int type) {
    // TODO
}

void test() {
    Repeater* forward = CreateRepeater(0);
    assert(forward != nullptr);

    std::ostringstream output;
    std::streambuf* oldBuffer = std::cout.rdbuf(output.rdbuf());
    forward->Play();
    delete forward;
    std::cout.rdbuf(oldBuffer);

    assert(output.str() ==
           "没想到你也是一个复读机\n"
           "正·复读机 炸了\n"
           "砰!\n");

    Repeater* reverse = CreateRepeater(1);
    assert(reverse != nullptr);

    std::ostringstream reverseOutput;
    oldBuffer = std::cout.rdbuf(reverseOutput.rdbuf());
    reverse->Play();
    delete reverse;
    std::cout.rdbuf(oldBuffer);

    assert(reverseOutput.str() ==
           "机读复个一是也你到想没\n"
           "机读复·反 炸了\n"
           "砰!\n");

    assert(CreateRepeater(2) == nullptr);
}

int main() {
    test();
    std::cout << "本关测试通过" << std::endl;
    return 0;
}

测试说明

type == 0 的完整预期输出为:

没想到你也是一个复读机
正·复读机 炸了
砰!

其中后两行验证了通过基类指针删除对象时,派生类析构函数先于基类析构函数执行。


开始你的任务吧,祝你成功!