HM140 虚析构释放子对象

HM140 虚析构释放子对象

HM140 虚析构释放子对象

来源: 第 140 集 类和对象-多态-虚析构和纯虚析构

难度: 入门

时间限制: 1s

空间限制: 64MB

题目描述

多态里常用父类指针指向堆上的子类对象,最后 delete 父类指针。若父类析构不是虚函数,这次 delete 不会走进子类析构。子类若把姓名 new 到堆上,这块内存就会泄露。

虚析构和**纯虚析构**都能解决「父类指针释放子对象不干净」:delete 时先走子类析构,再走父类析构(与构造相反:先有父再有子,离开时先走子再走父)。二者都**必须有函数体**。纯虚析构在类内写成 virtual ~Animal() = 0; 只是声明,还要在类外写出 Animal::~Animal() { ... };只声明不实现会在链接期报「无法解析的外部符号」。普通纯虚函数可以只有 = 0,纯虚析构不行。

区别:一旦有纯虚析构,这个类就是抽象类,栈上、堆上都不能实例化。只有虚析构、又没有其它纯虚函数时,基类仍可实例化。并不是每段代码都要写虚析构:子类没有堆区数据时可以不写。

请按下面场景实现,并用父类指针 new 出猫、调用 speak、再 delete

  • 基类构造输出 Animal的构造函数调用
  • 猫的构造把姓名放到堆上的 string*,并输出 Cat的构造函数调用
  • speak 输出「姓名 + 小猫在说话」,例如 汤姆小猫在说话
  • 猫的析构释放姓名并输出 Cat的析构函数调用
  • 普通/虚析构的基类析构输出 Animal的析构函数调用
  • 纯虚析构的实现输出 Animal的纯虚析构函数调用

读入若干操作,比较「无虚析构 / 虚析构 / 纯虚析构」。

输入格式

第一行一个整数 \(q\)(\(1 \le q \le 100\))。

接下来 \(q\) 行,每行一种操作:

  • NV s:基类析构**不是**虚函数,猫名为不含空白的 \(s\)(\(1 \le |s| \le 20\))
  • V s:基类使用虚析构,猫名为 \(s\)
  • PV s:基类使用纯虚析构(类外给出实现),猫名为 \(s\)
  • MAKE_PURE:询问此时基类能不能实例化
  • NEED_IMPL:询问纯虚析构要不要类外实现
  • NO_HEAP:询问子类没有堆区数据时,虚析构是否可以不写

输出格式

  • NV:三行,依次为基类构造、猫构造、说话;然后只有基类析构一行(猫的析构被跳过)
  • V:五行,构造两行、说话一行、先猫析构再基类析构
  • PV:五行,与 V 相同,但最后一行是 Animal的纯虚析构函数调用
  • MAKE_PURECANNOT
  • NEED_IMPLYES
  • NO_HEAPYES

每行行末换行。构造、说话、析构必须由对应函数打印。

样例

输入 #1

6
NV 汤姆
V 汤姆
PV 汤姆
MAKE_PURE
NEED_IMPL
NO_HEAP

输出 #1

Animal的构造函数调用
Cat的构造函数调用
汤姆小猫在说话
Animal的析构函数调用
Animal的构造函数调用
Cat的构造函数调用
汤姆小猫在说话
Cat的析构函数调用
Animal的析构函数调用
Animal的构造函数调用
Cat的构造函数调用
汤姆小猫在说话
Cat的析构函数调用
Animal的纯虚析构函数调用
CANNOT
YES
YES

输入 #2

1
V 加菲

输出 #2

Animal的构造函数调用
Cat的构造函数调用
加菲小猫在说话
Cat的析构函数调用
Animal的析构函数调用

说明

  • NV 少了猫的析构,堆上姓名没有释放,这就是要学虚析构的原因。
  • 虚析构与纯虚析构都能让 delete 父指针时先析构子类。
  • 有纯虚析构的类即使没有其它纯虚函数,也是抽象类。

信息

ID
1139
难度
(无)
分类
(无)
标签
(无)
递交数
0
已通过
0
通过率
?
上传者