实验4 组合与继承

实验任务1

代码

c++
 1 #pragma once
 2 
 3 #include <vector>
 4 #include <array>
 5 #include <string>
 6 
 7 class GradeCalc {
 8 public:
 9     GradeCalc(const std::string &cname);      
10     void input(int n);                         // 录入n个成绩
11     void output() const;                      // 输出成绩
12     void sort(bool ascending = false);        // 排序 (默认降序)
13     int min() const;                          // 返回最低分(如成绩未录入,返回-1)
14     int max() const;                          // 返回最高分 (如成绩未录入,返回-1)
15     double average() const;                   // 返回平均分 (如成绩未录入,返回0.0)
16     void info();                      // 输出课程成绩信息 
17 
18 private:
19     void compute();     // 成绩统计
20 
21 private:
22     std::string course_name;     // 课程名
23     std::vector<int> grades;     // 课程成绩
24     std::array<int, 5> counts;      // 保存各分数段人数([0, 60), [60, 70), [70, 80), [80, 90), [90, 100]
25     std::array<double, 5> rates;    // 保存各分数段人数占比 
26     bool is_dirty;      // 脏标记,记录是否成绩信息有变更
27 };
GradeCalc.hpp
  1 #include <algorithm>
  2 #include <array>
  3 #include <cstdlib>
  4 #include <iomanip>
  5 #include <iostream>
  6 #include <numeric>
  7 #include <string>
  8 #include <vector>
  9 
 10 #include "GradeCalc.hpp"
 11 
 12 GradeCalc::GradeCalc(const std::string &cname):course_name{cname},is_dirty{true} {
 13     counts.fill(0);
 14     rates.fill(0);   
 15 }
 16 
 17 void GradeCalc::input(int n) {
 18     if(n < 0) {
 19         std::cerr << "无效输入! 人数不能为负数\n";
 20         std::exit(1);
 21     }
 22 
 23     grades.reserve(n);
 24 
 25     int grade;
 26 
 27     for(int i = 0; i < n;) {
 28         std::cin >> grade;
 29 
 30         if(grade < 0 || grade > 100) {
 31             std::cerr << "无效输入! 分数须在[0,100]\n";
 32             continue;
 33         }
 34         
 35         grades.push_back(grade);
 36         ++i;
 37     }
 38 
 39     is_dirty = true;  // 设置脏标记:成绩信息有变更
 40 }
 41 
 42 void GradeCalc::output() const {
 43     for(auto grade: grades)
 44         std::cout << grade << ' ';
 45     std::cout << std::endl;
 46 }
 47     
 48 void GradeCalc::sort(bool ascending) {
 49     if(ascending)
 50         std::sort(grades.begin(), grades.end());
 51     else
 52         std::sort(grades.begin(), grades.end(), std::greater<int>());
 53 }
 54 
 55 int GradeCalc::min() const {
 56     if(grades.empty())
 57         return -1;
 58 
 59     auto it = std::min_element(grades.begin(), grades.end());
 60     return *it;
 61 }
 62 
 63 int GradeCalc::max() const {
 64     if(grades.empty()) 
 65         return -1;
 66 
 67     auto it = std::max_element(grades.begin(), grades.end());
 68     return *it;
 69 }
 70 
 71 double GradeCalc::average() const {
 72     if(grades.empty())
 73         return 0.0;
 74 
 75     double avg = std::accumulate(grades.begin(), grades.end(), 0.0)/grades.size();
 76     return avg;
 77 }
 78 
 79 void GradeCalc::info() {
 80     if(is_dirty) 
 81        compute();
 82 
 83     std::cout << "课程名称:\t" << course_name << std::endl;
 84     std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << std::endl;
 85     std::cout << "最高分:\t" << max() << std::endl;
 86     std::cout << "最低分:\t" << min() << std::endl;
 87 
 88     const std::array<std::string, 5> grade_range{"[0, 60) ", 
 89                                            "[60, 70)", 
 90                                            "[70, 80)",
 91                                            "[80, 90)", 
 92                                            "[90, 100]"};
 93     
 94     for(int i = static_cast<int>(grade_range.size())-1; i >= 0; --i)
 95         std::cout << grade_range[i] << "\t: " << counts[i] << "人\t"
 96                   << std::fixed << std::setprecision(2) << rates[i]*100 << "%\n";
 97 }
 98 
 99 void GradeCalc::compute() {
100     if(grades.empty())
101         return;
102 
103     counts.fill(0); 
104     rates.fill(0.0);
105 
106     // 统计各分数段人数
107     for(auto grade:grades) {
108         if(grade < 60)
109             ++counts[0];        // [0, 60)
110         else if (grade < 70)
111             ++counts[1];        // [60, 70)
112         else if (grade < 80)
113             ++counts[2];        // [70, 80)
114         else if (grade < 90)
115             ++counts[3];        // [80, 90)
116         else
117             ++counts[4];        // [90, 100]
118     }
119 
120     // 统计各分数段比例
121     for(size_t i = 0; i < rates.size(); ++i)
122         rates[i] = counts[i] * 1.0 / grades.size();
123     
124     is_dirty = false;  // 更新脏标记
125 }
GradeCalc.cpp
 1 #include <iostream>
 2 #include <string>
 3 #include "GradeCalc.hpp"
 4 
 5 void test() {
 6     GradeCalc c1("OOP");
 7 
 8     std::cout << "录入成绩:\n";
 9     c1.input(5);
10 
11     std::cout << "输出成绩:\n";
12     c1.output();
13 
14     std::cout << "排序后成绩:\n";
15     c1.sort(); c1.output();
16 
17     std::cout << "*************成绩统计信息*************\n";
18     c1.info();
19 
20 }
21 
22 int main() {
23     test();
24 }
task1.cpp

运行测试截图

图片

问题回答

问题1:组合关系识别
GradeCalc 类声明中, 逐行写出所有体现"组合"关系的成员声明,并用一句话说明每个被组合对象的功能。

1、std::string course_name;  组合了一个字符串对象,用于存储课程名称,是课程类的组成部分。

2、std::vector<int> grades;  组合了一个动态整型数组,用于保存录入的所有学生成绩。

3、std::array<int,5>counts;  组合了一个长度为5的整型数组,用于统计各分数段的人数。

4、std::array<double,5> rates;  组合了一个长度为5的双精度浮点数组,用于保存各分数段人数占比。


问题2:接口暴露理解
如在 test 模块中这样使用(GradeCalc c("OOP");c.input(5);c.push_back(97); ),是否合法?如不合法,解释原因。

不合法

因为GradeCalc 没有提供名为 push_back的公共接口。

 

问题3:架构设计分析
当前设计方案中, compute info 模块中调用:
1)连续打印3次统计信息, compute 会被调用几次?标记 is_dirty 起到什么作用?

如果这三次打印之间没有修改成绩,则 compute() 只会在第一次info() 时被调用一次。

is_dirty记录成绩数据是否发生变更。只有当数据变更即is_dirty==true时,才需要重新执行统计计算。

2)如新增 update_grade(index, new_grade) ,这种设计需要更改 compute 调用位置吗?简洁说明理由。

不需要。

只需要在每次更改grade信息后将is_dirty的值置为true,就可以在下次调用info()时更新统计数组。


问题4:功能扩展设计
要增加"中位数"统计, 不新增数据成员怎么做?在哪个函数里加?写出伪代码。

在info()函数中添加。

伪代码(在info()函数中compute();之后添加):

 

int n = grades.size();
int middle;

 

if (n % 2 == 1)
middle= grades[n / 2 ];

 

else
middle= (grades[n / 2-1] + grades[n / 2 ]) * 0.5;

std::cout << "中位数:\t" << middle<< std::endl;

 

问题5:数据状态管理
GradeCalc compute 中都包含代码: counts.fill(0); rates.fill(0);
compute 中能否去掉这两行?如去掉,在哪种使用场景下会引发统计错误?

不能。若去掉,会在多次调用 info()或多次录入会导致累计错误。


问题6:内存管理理解
input 模块中代码 grades.reserve(n); 如果去掉:
1)对程序功能有影响吗?(去掉重新编译、运行,观察功能是否受影响)

没有
2)对性能有影响吗?如有影响,用一句话陈述具体影响。
有。

导致多次内存重分配,造成不必要的内存复制开销,降低性能。

实验任务2

代码

c++
 1 #pragma once
 2 
 3 #include <array>
 4 #include <string>
 5 #include <vector>
 6 
 7 class GradeCalc: private std::vector<int> {
 8 public:
 9     GradeCalc(const std::string &cname);      
10     void input(int n);                        // 录入n个成绩
11     void output() const;                      // 输出成绩
12     void sort(bool ascending = false);        // 排序 (默认降序)
13     int min() const;                          // 返回最低分
14     int max() const;                          // 返回最高分
15     double average() const;                   // 返回平均分
16     void info();                              // 输出成绩统计信息 
17 
18 private:
19     void compute();               // 计算成绩统计信息
20 
21 private:
22     std::string course_name;     // 课程名
23     std::array<int, 5> counts;   // 保存各分数段人数([0, 60), [60, 70), [70, 80), [80, 90), [90, 100]
24     std::array<double, 5> rates; // 保存各分数段占比
25     bool is_dirty;      // 脏标记,记录是否成绩信息有变更
26 };
GradeCalc.hpp
  1 #include <algorithm>
  2 #include <array>
  3 #include <cstdlib>
  4 #include <iomanip>
  5 #include <iostream>
  6 #include <numeric>
  7 #include <string>
  8 #include <vector>
  9 #include "GradeCalc.hpp"
 10 
 11 
 12 GradeCalc::GradeCalc(const std::string &cname): course_name{cname}, is_dirty{true}{
 13     counts.fill(0);
 14     rates.fill(0);
 15 }   
 16 
 17 void GradeCalc::input(int n) {
 18     if(n < 0) {
 19         std::cerr << "无效输入! 人数不能为负数\n";
 20         return;
 21     }
 22 
 23     this->reserve(n);
 24 
 25     int grade;
 26 
 27     for(int i = 0; i < n;) {
 28         std::cin >> grade;
 29         if(grade < 0 || grade > 100) {
 30             std::cerr << "无效输入! 分数须在[0,100]\n";
 31             continue;
 32         }
 33 
 34         this->push_back(grade);
 35         ++i;
 36     } 
 37 
 38     is_dirty = true;
 39 }  
 40 
 41 void GradeCalc::output() const {
 42     for(auto grade: *this)
 43         std::cout << grade << ' ';
 44     std::cout << std::endl;
 45 } 
 46 
 47 void GradeCalc::sort(bool ascending) {
 48     if(ascending)
 49         std::sort(this->begin(), this->end());
 50     else
 51         std::sort(this->begin(), this->end(), std::greater<int>());
 52 }  
 53 
 54 int GradeCalc::min() const {
 55     if(this->empty())
 56         return -1;
 57 
 58     return *std::min_element(this->begin(), this->end());
 59 }  
 60 
 61 int GradeCalc::max() const {
 62     if(this->empty())
 63         return -1;
 64 
 65     return *std::max_element(this->begin(), this->end());
 66 }    
 67 
 68 double GradeCalc::average() const {
 69     if(this->empty())
 70         return 0.0;
 71 
 72     double avg = std::accumulate(this->begin(), this->end(), 0.0) / this->size();
 73     return avg;
 74 }   
 75 
 76 void GradeCalc::info() {
 77     if(is_dirty) 
 78         compute();
 79 
 80     std::cout << "课程名称:\t" << course_name << std::endl;
 81     std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << std::endl;
 82     std::cout << "最高分:\t" << max() << std::endl;
 83     std::cout << "最低分:\t" << min() << std::endl;
 84 
 85     const std::array<std::string, 5> grade_range{"[0, 60) ", 
 86                                            "[60, 70)", 
 87                                            "[70, 80)",
 88                                            "[80, 90)", 
 89                                            "[90, 100]"};
 90     
 91     for(int i = static_cast<int>(grade_range.size())-1; i >= 0; --i)
 92         std::cout << grade_range[i] << "\t: " << counts[i] << "人\t"
 93                   << std::fixed << std::setprecision(2) << rates[i]*100 << "%\n";
 94 }
 95 
 96 void GradeCalc::compute() {
 97     if(this->empty())
 98         return;
 99     
100     counts.fill(0);
101     rates.fill(0);
102 
103     // 统计各分数段人数
104     for(int grade: *this) {
105         if(grade < 60)
106             ++counts[0];        // [0, 60)
107         else if (grade < 70)
108             ++counts[1];        // [60, 70)
109         else if (grade < 80)
110             ++counts[2];        // [70, 80)
111         else if (grade < 90)
112             ++counts[3];        // [80, 90)
113         else
114             ++counts[4];        // [90, 100]
115     }
116 
117     // 统计各分数段比例
118     for(size_t i = 0; i < rates.size(); ++i)
119         rates[i] = counts[i] * 1.0 / this->size();
120     
121     is_dirty = false;
122 }
GradeCalc.cpp
 1 #include <iostream>
 2 #include <string>
 3 #include "GradeCalc.hpp"
 4 
 5 void test() {
 6     GradeCalc c1("OOP");
 7 
 8     std::cout << "录入成绩:\n";
 9     c1.input(5);
10 
11     std::cout << "输出成绩:\n";
12     c1.output();
13 
14     std::cout << "排序后成绩:\n";
15     c1.sort(); c1.output();
16 
17     std::cout << "*************成绩统计信息*************\n";
18     c1.info();
19 
20 }
21 
22 int main() {
23     test();
24 }
task2.cpp

运行测试截图

图片

问题回答

问题1:继承关系识别
写出 GradeCalc 类声明体现"继承"关系的完整代码行。
 class GradeCalc : private std::vector<int> {};
 
问题2:接口暴露理解
当前继承方式下,基类 vector<int> 的接口会自动成为 GradeCalc 的接口吗?
如在 test 模块中这样用,能否编译通过?用一句话解释原因。
 会。
当前继承方式是 private 继承,vector<int> 的接口会自动成为 GradeCalc 的私有成员函数,由于私有继承,该函数在类外部不可见、不可调用。
 
问题3:数据访问差异
对比继承方式与组合方式内部实现数据访问的一行典型代码。说明两种方式下的封装差异带来的数据访问接口差
组合方式:通过成员变量 grades(一个vector<int>对象)进行访问,需显式命名成员,封装性强,外部不感知内部结构

继承方式:利用从基类继承的接口,可以直接使用 this 指针进行访问,但容易暴露底层实现细节,破坏封装性。

 

问题4:组合 vs. 继承方案选择
你认为组合方案和继承方案,哪个更适合成绩计算这个问题场景?简洁陈述你的结论和理由。
组合方式。
封装性更强,组合允许隐藏vector实现细节,只暴露必要的接口,防止外部随意修改成绩列表;可维护性和扩展性更好,若将来需要更换存储结构,组合方式只需修改内部成员,不影响接口。

实验任务3

代码

c++
 1 #pragma once
 2 
 3 #include <string>
 4 #include <vector>
 5 
 6 enum class GraphType {circle, triangle, rectangle};
 7 
 8 // Graph类定义
 9 class Graph {
10 public:
11     virtual void draw() {}
12     virtual ~Graph() = default;
13 };
14 
15 // Circle类声明
16 class Circle : public Graph {
17 public:
18     void draw();
19 };
20 
21 // Triangle类声明
22 class Triangle : public Graph {
23 public:
24     void draw();
25 };
26 
27 // Rectangle类声明
28 class Rectangle : public Graph {
29 public:
30     void draw();
31 };
32 
33 // Canvas类声明
34 class Canvas {
35 public:
36     void add(const std::string& type);   // 根据字符串添加图形
37     void paint() const;                  // 使用统一接口绘制所有图形
38     ~Canvas();                           // 手动释放资源
39 
40 private:
41     std::vector<Graph*> graphs;          
42 };
43 
44 // 4. 工具函数
45 GraphType str_to_GraphType(const std::string& s);  // 字符串转枚举类型
46 Graph* make_graph(const std::string& type);  // 创建图形,返回堆对象指针
Graph.hpp
 1 #include <algorithm>
 2 #include <cctype>
 3 #include <iostream>
 4 #include <string>
 5 
 6 #include "Graph.hpp"
 7 
 8 // Circle类实现
 9 void Circle::draw()     { std::cout << "draw a circle...\n"; }
10 
11 // Triangle类实现
12 void Triangle::draw()   { std::cout << "draw a triangle...\n"; }
13 
14 // Rectangle类实现
15 void Rectangle::draw()  { std::cout << "draw a rectangle...\n"; }
16 
17 // Canvas类实现
18 void Canvas::add(const std::string& type) {
19     Graph* g = make_graph(type);
20     if (g) 
21         graphs.push_back(g);
22 }
23 
24 void Canvas::paint() const {
25     for (Graph* g : graphs) 
26         g->draw();   
27 }
28 
29 Canvas::~Canvas() {
30     for (Graph* g : graphs) 
31         delete g;
32 }
33 
34 // 工具函数实现
35 // 字符串 → 枚举转换
36 GraphType str_to_GraphType(const std::string& s) {
37     std::string t = s;
38     std::transform(s.begin(), s.end(), t.begin(),
39                    [](unsigned char c) { return std::tolower(c);});
40 
41     if (t == "circle")   
42         return GraphType::circle;
43 
44     if (t == "triangle") 
45         return GraphType::triangle;
46 
47     if (t == "rectangle")
48         return GraphType::rectangle;
49 
50     return GraphType::circle;   // 缺省返回
51 }
52 
53 // 创建图形,返回堆对象指针
54 Graph* make_graph(const std::string& type) {
55     switch (str_to_GraphType(type)) {
56     case GraphType::circle:     return new Circle;
57     case GraphType::triangle:   return new Triangle;
58     case GraphType::rectangle:  return new Rectangle;
59     default: return nullptr;
60     }
61 }
Graph.cpp
 1 #include <string>
 2 #include "Graph.hpp"
 3 
 4 void test() {
 5     Canvas canvas;
 6 
 7     canvas.add("circle");
 8     canvas.add("triangle");
 9     canvas.add("rectangle");
10     canvas.paint();
11 }
12 
13 int main() {
14     test();
15 }
task3.cpp

运行测试截图

图片

问题回答

问题1:对象关系识别
(1)写出Graph.hpp中体现"组合"关系的成员声明代码行,并用一句话说明被组合对象的功能。
std::vector<Graph*> graphs;
graphs组合了一组指向图形对象的指针,用于在画布(Canvas)中管理多个不同类型的图形,实现统一绘制。
 
(2)写出Graph.hpp中体现"继承"关系的类声明代码行。

class Circle : public Graph {};
class Triangle : public Graph {};
class Rectangle : public Graph {};

 

问题2:多态机制观察
(1) Graph 中的 draw 若未声明成虚函数, Canvas::paint() 中 g->draw() 运行结果会有何不同?
运行失去多态性,将始终调用基类中的draw()函数,而无法调用各自派生类中的draw()函数,函数功能无法实现。
(2)若 Canvas 类 std::vector<Graph*> 改成 std::vector<Graph> ,会出现什么问题?

多态失效,容器中存储的全是 Graph 类型对象,不再是原始的 Circle ;

只调用基类中的draw()函数,无法体现具体图形行为。

(3)若 ~Graph() 未声明成虚函数,会带来什么问题?
 会导致析构不完整,Circle中的资源将永远无法被释放,造成内存泄露。
 
问题3:扩展性思考
若要新增星形 Star ,需在哪些文件做哪些改动?逐一列出。
Graph.hpp中:

在 enum class GraphType 中添加新枚举值(Star);

声明新的派生类 Star;

Graph.cpp中:

实现Star::draw();

修改 str_to_GraphType函数,增加对 "Star"的判断;

修改 make_graph 函数,添加 case 分支;

main.cpp中:

在 test() 中添加测试:canvas.add("Star");

 

问题4:资源管理
观察 make_graph 函数和 Canvas 析构函数:
(1) make_graph 返回的对象在什么地方被释放?
 在 Canvas 的析构函数中被释放
(2)使用原始指针管理内存有何利弊?
 
利:简单直观,控制直接,不依赖智能指针,兼容性强。
弊:容易导致内存泄漏,异常安全差, 不支持自动移动语义, 多人协作时易出错,维护成本高。
 

实验任务4

代码

c++
 1 #ifndef TOY_HPP
 2 #define TOY_HPP
 3 
 4 #include <iostream>
 5 #include <string>
 6 #include <vector>
 7 
 8 using namespace std;
 9 
10 // 抽象基类:毛绒玩具
11 class Toy {
12 protected:
13     string name;    // 玩具名称
14     string type;    // 玩具类型
15     string color;   // 颜色
16     float price;    // 价格
17     string brand;   //品牌 
18     int energy;     //电量 
19 public:
20     Toy(string n, string t, string c, float p,string b,int e); 
21     virtual ~Toy() = default;  // 虚析构函数
22 
23     // 特异功能
24     virtual void specialFunction() const = 0;
25 
26     // 获取信息(非虚函数)
27     string getName() const ; 
28     string getType() const ; 
29     string getColor() const;
30     float getPrice() const;
31     string getBrand() const; 
32     int getEnergy() const;
33 };
34 
35 // 发声玩具
36 class SoundToy : public Toy {
37 private:
38     string soundContent;  // 发声内容
39 public:
40     SoundToy(string n, string t, string c, float p, string b,int e,string sc);    
41     // 发声
42     void specialFunction() const override;
43 };
44 
45 //发光玩具
46 class LightToy : public Toy {
47 private:
48     string lightColor;  // 发光颜色
49 public:
50     LightToy(string n, string t, string c, float p, string b,int e,string lc);
51 // 发光
52     void specialFunction() const override;
53 };
54 
55 //跳舞玩具
56 class DanceToy : public Toy{
57 private:
58     string danceType;  //舞种
59 public:
60     DanceToy(string n, string t, string c, float p, string b,int e, string dt);
61 // 发光
62     void specialFunction() const override;
63 }; 
64 
65 //唱歌玩具
66 class SingToy : public Toy{
67 private:
68     string singSong;  //歌名 
69 public:
70     SingToy(string n, string t, string c, float p, string b,int e,string ss);
71 
72     void specialFunction() const override;
73 }; 
74 
75 // 玩具工厂类(组合一组Toy对象)
76 class ToyFactory {
77 private:
78     vector<Toy*> toys;  // 管理多个Toy指针
79 public:
80     ~ToyFactory();  // 析构
81 
82     // 添加玩具
83     void addToy(Toy* t) ;
84 
85     // 显示所有玩具信息
86     void displayAllToys() const;
87     //测试特异功能
88     void testAllSpecialFunctions() const;
89 };
90 
91 #endif
Toy.hpp
 1 #include "Toy.hpp"
 2 #include<iostream>
 3 #include<vector>
 4 
 5 using namespace std;
 6 
 7 //Toy
 8 Toy::Toy(string n, string t, string c, float p,string b,int e) : name(n), type(t), color(c), price(p),brand(b),energy(e) {}
 9 
10 string Toy::getName() const {
11     return name;
12 }
13 string Toy::getType() const { 
14     return type; 
15 }
16 string Toy::getColor() const {
17     return color;
18 }
19 float Toy::getPrice() const {
20     return price;
21 }
22 string Toy::getBrand() const {
23     return brand; 
24 }
25 int Toy::getEnergy() const {
26     return energy; 
27 }
28 
29 //发声Toy
30 SoundToy::SoundToy(string n, string t, string c, float p, string b,int e,string sc): Toy(n, t, c, p,b,e), soundContent(sc) {}
31     
32 void SoundToy::specialFunction() const  {
33         cout << "" << name << "】Say:" << soundContent << endl;
34     
35 }; 
36 
37 //发光Toy 
38 LightToy::LightToy(string n, string t, string c, float p, string b,int e,string lc): Toy(n, t, c, p,b,e), lightColor(lc) {}
39 
40 void LightToy::specialFunction() const  {
41         cout << "" << name << "】发出" << lightColor << "的光" << endl;
42 }
43     
44 //跳舞Toy
45 DanceToy::DanceToy(string n, string t, string c, float p, string b,int e,string dt): Toy(n, t, c, p,b,e), danceType(dt) {}
46 
47 void DanceToy::specialFunction() const  {
48         cout << "" << name << "】准备跳" <<danceType<< endl;
49 }
50 
51 //唱歌Toy
52 SingToy::SingToy(string n, string t, string c, float p, string b,int e,string ss): Toy(n, t, c, p,b,e), singSong(ss) {}
53 
54 void SingToy::specialFunction() const  {
55         cout << "" << name << "】打算唱一首你喜欢的:" <<singSong<< endl;
56 }
57     
58 //玩具工厂 
59 ToyFactory::~ToyFactory(){  
60         for (auto t : toys) delete t;
61     }
62     
63 void ToyFactory::addToy(Toy* t) { 
64     toys.push_back(t); 
65 }
66 
67 void ToyFactory::displayAllToys() const {
68         cout << "===== 玩具工厂清单 =====" << endl;
69         cout<<endl;
70         if(toys.empty()){
71             cout<<"工厂暂无玩具。\n";
72             return;
73         }
74         for (const auto t : toys) {
75             cout << "名称:" << t->getName() 
76                  << " | 类型:" << t->getType()
77                  << " | 颜色:" << t->getColor()
78                  << " | 价格:" << t->getPrice() << ""
79                  << " | 品牌:" << t->getBrand()
80                  << " | 电量:" << t->getEnergy() <<"%"<< endl;
81             cout << "------------------------------------------------------------------------------------" << endl;
82         }
83 }
84 void ToyFactory::testAllSpecialFunctions() const {
85     cout<<"\n=====启动玩具工厂测试系统=====\n";
86     cout<<endl;
87     if(toys.empty()){
88             cout<<"工厂暂无可测试玩具。\n";
89             return;
90         }
91      for (size_t i = 0; i < toys.size(); ++i) {
92         cout << "[" << (i+1) << "] ";
93         toys[i]->specialFunction();  // 多态调用
94     }
95     cout<<endl;
96     cout<<"=====所有玩具测试完毕!=====\n" ;
97 }
Toy.cpp
 1 #include <iostream>
 2 #include "Toy.hpp"
 3 using namespace std;
 4 
 5 int main() {
 6     // 创建工厂
 7     ToyFactory factory;
 8     // 添加不同类型的玩具
 9     factory.addToy(new SoundToy("HeartBear", "毛绒公仔", "棕色", 59.9,
10                                 "Jelly", 100, "“今天天气很好,有没有见到想见的人^_^”"));
11     factory.addToy(new LightToy("ShiningRabbit", "毛绒挂件", "白色", 29.9,
12                                 "Stef", 96, "暖黄色"));
13     factory.addToy(new DanceToy("PulsatileKitten", "跳舞玩偶", "灰色", 89.9, 
14                                 "Jelly",43,"爵士舞"));
15     factory.addToy(new SingToy("Puppy", "毛绒公仔", "黄色", 129.9, 
16                                 "Jelly",66,"《这条小鱼在乎》"));
17 
18     // 显示所有玩具信息并测试功能
19     factory.displayAllToys();
20     factory.testAllSpecialFunctions();
21 
22     return 0;
23 }
task4.cpp

 

运行测试截图

image

问题描述

上述程序设计了一个面向对象的玩具管理系统,模拟一家生产多功能电子毛绒玩具的工厂,实现对不同类型玩具的信息管理与功能测试。

1、定义了抽象的“毛绒玩具”基类,封装通用属性(玩具名称、玩具类型、颜色、价格、品牌和电量);

2、通过继承机制派生出具有不同特异功能的子类(发声玩具、发光玩具、跳舞玩具和唱歌玩具);

3、利用虚函数和多态机制,使用统一接口调用各玩具的特异功能;

4、设计“玩具工厂”类,通过组合方式管理多个玩具对象;

5、实现两个独立功能模块:显示所有玩具的基本信息和测试所有玩具的特异功能;

6、同时便于系统拓展,支持后续添加新类型的玩具。

对象关系

1、SoundToy、LightToy、DanceToy、SingToy均继承自 Toy基类,共享其数据成员和方法,体现“is-a”关系。

2、ToyFactory 类包含一个 vector<Toy*> 成员,用于管理多个玩具指针,体现“has-a”关系。

 

posted @ 2025-11-29 23:24  Wenteen  阅读(2)  评论(0)    收藏  举报