实验4 组合与继承
实验任务1
运行结果截图如下

问题1
GradeCalc 类声明中,体现"组合"关系的成员声明及对应功能如下:
std::vector<int> grades; grades用于存储所有成绩数据
std::array<int, 5> counts; counts用于统计各分数段人数
std::array<double, 5> rates; rates用于统计各分数段比例
问题2
首先要指出题目中把"input"拼成了"inupt"。
不合法。因为push_back() 是 std::vector 的方法,不是 GradeCalc 的公开接口。而grades 是私有成员,外部无法直接访问,故只能通过 GradeCalc 提供的 input() 方法添加成绩。
问题3
(1)compute连续打印3次,只调用1次。
is_dirty用来标记数据是否被修改,避免重复计算
(2)不需要更改compute调用位置。因为本来数据的变更就在计算前处理,新增后仍保持对数据统一计算。
问题4
可以在info()函数中插入一段
void GradeCalc::info() { if (is_dirty) compute(); // ... 原有的输出代码 ... std::cout << "中位数:\t" << std::fixed << std::setprecision(2); if (grades.empty()) { std::cout << "0.00" << std::endl; } else { std::vector<int> temp = grades; std::sort(temp.begin(), temp.end()); size_t n = temp.size(); if (n % 2 == 1) { std::cout << static_cast<double>(temp[n / 2]) << std::endl; } else { std::cout << (temp[n / 2 - 1] + temp[n / 2]) / 2.0 << std::endl; } } // ... 继续原有的输出 ... }
问题5
不能去掉。去掉就会导致数组无法正常初始化,统计结果会包含前一次的数据,多次调用compute()会导致数据累加。
问题6
功能上无影响;去掉后不会改变程序的功能,程序仍能正常运行且正常输入输出。
性能上有影响;因为去掉后push_back() 在自动分配内存以保证功能正常实现,这也意味着程序在反复扩充内存,进而影响性能。
实验任务2
运行结果截图如下

问题1
class GradeCalc : private std::vector<int>
问题2
会,合法。能。当GradeCalc公有继承自std::vector<int>,则其继承了基类的所有公有接口,而push_back()是std::vector的公有方法。
问题3
封装差异:组合方式完全封装了底层容器,继承方式暴露了部分容器特性。
接口差异:组合方式必须提供自己的接口,继承方式可以直接使用基类接口。
问题4
选择组合。因为程序的功能是实现成绩计算器,其"有"成绩列表,不要求"是"列表,用组合的方式去写,可以直观地编写不同的类。同时用组合只暴露必要的接口,防止外部调用继承的私有函数。
实验任务3
运行结果截图如下

问题1
(1)std::vector<Graph*> graphs; 存储图形的指针
(2)
class Circle : public Graph
class Triangle : public Graph
class Rectangle : public Graph
问题2
(1)g->draw() 只会调用基类的 draw()
结果输出"draw a...",无法实现多态
(2) 会导致派生类的特有信息丢失,只能存储基类部分。
(3) 派生类资源无法释放。
问题3
在GraphType枚举中添加star
在enum之后添加Star类声明
实现Star::draw()
修改str_to_GraphType函数
GraphType str_to_GraphType(const std::string& s) { if (s == "circle") return GraphType::circle; // ... 其他类型 if (s == "star") return GraphType::star; throw std::invalid_argument("未知图形类型"); }
修改make_graph函数
Graph* make_graph(GraphType t) { switch(t) { case GraphType::circle: return new Circle(); // ... 其他类型 case GraphType::star: return new Star(); } }
问题4
(1) 在 Canvas 的析构函数中释放
(2)
利:简单直接,没有额外开销
弊:需要手动管理释放,易导致内存泄漏
实验任务4
源代码如下
toy.hpp
#ifndef TOY_HPP #define TOY_HPP #include <iostream> #include <string> #include <vector> class Toy { public: Toy(const std::string& name, const std::string& type, const std::string& color) : name(name), type(type), color(color) {} virtual ~Toy() = default; void displayInfo() const { std::cout << "玩具名称:" << name << std::endl; std::cout << "玩具类型:" << type << std::endl; std::cout << "玩具颜色:" << color << std::endl; } virtual void specialfunc() const = 0; private: std::string name; std::string type; std::string color; }; class FurinaDoll : public Toy { public: FurinaDoll(const std::string& name, const std::string& color) : Toy(name, "Q版玩偶", color) {} void specialfunc() const override; }; class MawikaDoll : public Toy { public: MawikaDoll(const std::string& name, const std::string& color) : Toy(name, "解压玩具", color) {} void specialfunc() const override; }; class ToyFactory { public: ToyFactory() = default; ~ToyFactory() { for (auto toy : toystore) delete toy; } void addtoy(Toy* toy); void displayalltoys() const; private: std::vector<Toy*> toystore; }; #endif // TOY_HPP
toy.cpp
#include "toy.hpp" void FurinaDoll::specialfunc() const { std::cout << "特异功能:录音 放音" << std::endl; } void MawikaDoll::specialfunc() const { std::cout << "特异功能:受到外力作用后能做出较大形变后复原" << std::endl; } void ToyFactory::addtoy(Toy* toy) { toystore.push_back(toy); } void ToyFactory::displayalltoys() const { for (const auto& toy : toystore) { toy->displayInfo(); toy->specialfunc(); std::cout << "---" << std::endl; } }
main.cpp
#include "toy.hpp" int main() { ToyFactory factory; factory.addtoy(new FurinaDoll("芙宁娜玩偶", "蓝")); factory.addtoy(new FurinaDoll("芙宁娜玩偶", "白")); factory.addtoy(new MawikaDoll("玛威卡人偶", "红")); factory.displayalltoys(); return 0; }
运行结果截图如下

总结
这次主要试验了组合和继承的设计的不同,虚函数实现多态。因为这种关联性,在编写时要格外注意细节。但是应用到实际从0编写的时候,容易迷失方向。在任务4里我花了很长时间才写出一个丐版的只有一个玩具类的程序(中途求助了一下我们班的学委),不过有了一个之后就很好类比着去扩写了。

浙公网安备 33010602011771号