练习:使用虚析构函数管理复读机对象¶
任务描述¶
设计基类 Repeater、正向复读机类 ForRepeater 和反向复读机类 RevRepeater,并实现工厂函数 CreateRepeater。程序通过 Repeater* 调用不同派生类的 Play,再通过同一基类指针删除对象。
相关知识¶
基类指针与虚函数¶
基类指针可指向派生类对象。若 Play 是虚函数,通过 Repeater* 调用时会根据对象实际类型选择最后覆盖的版本:
虚析构函数¶
基类指针用于销毁派生对象时,基类析构函数必须为虚函数:
这样执行 delete repeater; 时,先调用派生类析构函数,再调用 Repeater 析构函数。若缺少 virtual,派生类的清理工作可能不会执行。
工厂函数¶
CreateRepeater 将创建逻辑集中起来,根据类型返回基类指针。调用方只依赖 Repeater 接口,不需要关心对象的具体派生类型。
编程要求¶
- 使用 C++11 标准编写单文件程序。
- 声明基类
Repeater:
Play 不输出内容;析构函数输出 砰!。
ForRepeater公有继承Repeater,覆盖Play输出没想到你也是一个复读机,析构函数输出正·复读机 炸了。RevRepeater公有继承Repeater,覆盖Play输出机读复个一是也你到想没,析构函数输出机读复·反 炸了。- 完成普通函数:
当 type == 0 时创建 ForRepeater;当 type == 1 时创建 RevRepeater;其他值返回 nullptr。
- 在
test中创建两种复读机,捕获Play与delete的输出并断言;同时验证非法类型返回空指针。
待完成代码¶
#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 的完整预期输出为:
其中后两行验证了通过基类指针删除对象时,派生类析构函数先于基类析构函数执行。
开始你的任务吧,祝你成功!