OOP-实验4 - FF

news/2025/11/28 19:27:22/文章来源:https://www.cnblogs.com/JxLi/p/19274653

实验任务1

源代码task1

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

运行测试截图

task1

回答问题

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

std::string course_name;    存储课程名
std::vector<int> grades;      存储课程成绩
std::array<int, 5> counts;    存储各分数段人数
std::array<double, 5> rates;  存储各分数段人数占总人数的比例

问题2:如在 test 模块中这样使用,是否合法?如不合法,解释原因。

不合法;

原因:GradeCalc 类中并没有声明并定义 push_back() 方法,无法使用

问题3:当前设计方案中,compute 在 info 模块中调用:

(1)连续打印 3 次统计信息,compute 会被调用几次?标记 is_dirty 起到什么作用?

compute 只会被调用一次;

作用:当 is_dirty == true 时,会重新统计各分数段人数及其所占比例,更新数组;同时将 is_dirty 置为 false,避免重复统计

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

不需要;

理由:若添加成绩的更新方法,这一函数虽然修改了成绩容器,但只要在该方法中将 is_dirty 置为 true,将来调用 info() 时,会自动调用 compute() 更新统计数组

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

可以在 sort() 成员函数里添加;

伪代码:

double GradeCalc::sort(bool ascending)
{
    if (ascending)
        std::sort(grades.begin(), grades.end());

    else
        std::sort(grades.begin(), grades.end(), std::greater<int>());

    int n = grades.size();

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

    else
        return (grades[n / 2] + grades[n / 2 + 1]) * 0.5;
}

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

不能;

如果去掉,当成绩容器中数据被更新、is_dirty = true 时,会导致某些数据被重复计数

问题6:input 模块中代码 grades.reserve(n); 如果去掉:

(1)对程序功能有影响吗?(去掉重新编译、运行,观察功能是否受影响)

没有影响,可以正常编译、运行

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

有影响;

如果没有 reserve() 提前分配空间,在数据输入过程中 vector 会不断动态分配空间,性能下降

 

实验任务2

源代码task2

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

运行测试截图

task2

回答问题

问题1:写出 GradeCalc 类声明体现“继承”关系的完整代码行。

class GradeCalc : private std::vector<int> {};

问题2:当前继承方式下,基类 vector<int> 的接口会自动成为 GradeCalc 的接口吗?如在 test 模块中这样用,能否编译通过?用一句话解释原因。

不会;不能编译通过;

原因:当前继承方式是 private 继承,vector<int> 的接口会自动成为 GradeCalc 的私有成员函数,在类外部不能直接调用,无法通过编译

问题3:对比继承方式与组合方式内部实现数据访问的一行典型代码。说明两种方式下的封装差异带来的数据访问接口差异。

组合方式:通过私有数据成员 grades 访问,外部无法直接访问 

继承方式:利用从基类继承的接口,可以直接使用 this 指针进行访问

问题4:你认为组合方案和继承方案,哪个更适合成绩计算这个问题场景?简洁陈述你的结论和理由。

我认为组合方案更适合该问题场景;

理由:成绩计算符合 “has-a” 的关系,且组合方案具有更强的封装性,安全性更佳

 

实验任务3

源代码task3

 1 // Graph类、Circle类、Rectangle类、Triangle类、Canvas类声明
 2 
 3 #pragma once
 4 
 5 #include <string>
 6 #include <vector>
 7 
 8 enum class GraphType
 9 {
10     circle,
11     triangle,
12     rectangle
13 };
14 
15 // Graph类定义
16 class Graph
17 {
18 public:
19     virtual ~Graph() = default;
20     virtual void draw() {}
21 };
22 
23 // Circle类声明
24 class Circle : public Graph
25 {
26 public:
27     void draw();
28 };
29 
30 // Triangle类声明
31 class Triangle : public Graph
32 {
33 public:
34     void draw();
35 };
36 
37 // Rectangle类声明
38 class Rectangle : public Graph
39 {
40 public:
41     void draw();
42 };
43 
44 // Canvas类声明
45 class Canvas
46 {
47 public:
48     void add(const std::string &type); // 根据字符串添加图形
49     void paint() const;                // 使用统一接口绘制所有图形
50     ~Canvas();                         // 手动释放资源
51 
52 private:
53     std::vector<Graph *> graphs;
54 };
Graph.hpp
 1 // Graph类实现
 2 
 3 #include <algorithm>
 4 #include <cctype>
 5 #include <iostream>
 6 #include <string>
 7 
 8 #include "Graph.hpp"
 9 
10 // Circle类实现
11 void Circle::draw()
12 {
13     std::cout << "draw a circle..." << std::endl;
14 }
15 
16 // Triangle类实现
17 void Triangle::draw()
18 {
19     std::cout << "draw a triangle..." << std::endl;
20 }
21 
22 // Rectangle类实现
23 void Rectangle::draw()
24 {
25     std::cout << "draw a rectangle..." << std::endl;
26 }
27 
28 // Canvas类实现
29 Graph *make_graph(const std::string &s); // 前向声明
30 void Canvas::add(const std::string &type)
31 {
32     Graph *g = make_graph(type);
33     if (g)
34         graphs.push_back(g);
35 }
36 
37 void Canvas::paint() const
38 {
39     for (Graph *g : graphs)
40         g->draw();
41 }
42 
43 Canvas::~Canvas()
44 {
45     for (Graph *g : graphs)
46         delete g;
47 }
48 
49 // 工具函数实现
50 // 字符串 -> 枚举转换
51 GraphType str_to_GraphType(const std::string &s)
52 {
53     std::string t = s;
54 
55     std::transform(s.begin(), s.end(), t.begin(),
56                    [](unsigned char c)
57                    { return std::tolower(c); });
58 
59     if (t == "circle")
60         return GraphType::circle;
61 
62     if (t == "triangle")
63         return GraphType::triangle;
64 
65     if (t == "rectangle")
66         return GraphType::rectangle;
67 
68     return GraphType::circle; // 缺省返回
69 }
70 
71 // 创建图形,返回堆对象指针
72 Graph *make_graph(const std::string &type)
73 {
74     switch (str_to_GraphType(type))
75     {
76     case GraphType::circle:
77         return new Circle;
78 
79     case GraphType::triangle:
80         return new Triangle;
81 
82     case GraphType::rectangle:
83         return new Rectangle;
84 
85     default:
86         return nullptr;
87     }
88 }
Graph.cpp
 1 // 综合运用组合、继承、虚函数实现用一个接口打印不同图形
 2 
 3 #include <string>
 4 #include "Graph.hpp"
 5 #include "Graph.cpp"
 6 
 7 void test()
 8 {
 9     Canvas canvas;
10 
11     canvas.add("circle");
12     canvas.add("triangle");
13     canvas.add("rectangle");
14 
15     canvas.paint();
16 }
17 
18 int main()
19 {
20     test();
21 }
demo3.cpp

运行测试截图

屏幕截图 2025-11-27 201148

回答问题

问题1:对象关系识别

(1)写出 Graph.hpp 中体现“组合”关系的成员声明代码行,并用一句话说明被组合对象的功能。

std::vector<Graph *> graphs;  创建包含一组基类指针的容器 graphs,便于 Graph 及其派生类对象的统一管理

(2)写出 Graph.hpp 中体现“继承”关系的类声明代码行。

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

问题2:多态机制观察

(1)Graph 中的 draw 若未声明成虚函数,Canvas::paint() 中 g->draw() 运行结果会有何不同?

将会始终调用 Graph 基类的 draw() 函数,即没有输出;而不是调用各自派生类的重写函数

(2)若 Canvas 类 std::vector<Graph*> 改成 std::vector<Graph>,会出现什么问题?

传入的都是 Graph 对象,若调用 draw() 函数,也只会调用基类的 draw() 方法;

派生类各自的特点成员、方法也在将信息传递给 Graph 对象时丢失;

多态的设计初衷将失效,虚函数的设计也失去意义

(3)若 ~Graph() 未声明成虚函数,会带来什么问题?

调用析构函数释放空间时,仅仅会释放基类 Graph 的空间,而各自派生类的析构函数不会被调用,空间不会得到释放,导致内存泄露

问题3:若要新增星形 Star,需在哪些文件做哪些改动?逐一列出。

Graph.hpp:

enum class GraphType
{
    circle,
    triangle,
    rectangle,
    star
};
// Star类声明
class Star : public Graph
{
public:
    void draw();
};
 
Graph.cpp:
// Star类实现
void Star::draw()
{
    std::cout << "draw a star..." << std::endl;
}
 
GraphType str_to_GraphType(const std::string &s)
{
    std::string t = s;

    std::transform(s.begin(), s.end(), t.begin(),
                   [](unsigned char c)
                   { return std::tolower(c); });

    if (t == "circle")
        return GraphType::circle;

    if (t == "triangle")
        return GraphType::triangle;

    if (t == "rectangle")
        return GraphType::rectangle;

    if (t == "star")
        return GraphType::star;

    return GraphType::circle; // 缺省返回
}
 
Graph *make_graph(const std::string &type)
{
    switch (str_to_GraphType(type))
    {
    case GraphType::circle:
        return new Circle;

    case GraphType::triangle:
        return new Triangle;

    case GraphType::rectangle:
        return new Rectangle;

    case GraphType::star:
        return new Star;

    default:
        return nullptr;
    }
}

问题4:观察 make_graph 函数和 Canvas 析构函数:

(1)make_graph 返回的对象在什么地方被释放?

在类 Canvas 的析构函数 ~Canvas() 中被释放;遍历容器 graphs 依次释放

(2)使用原始指针管理内存有何利弊?

优点:便于理解,适用于简单的开发场景

缺点:若不小心忘记了释放空间会导致内存的泄露;手动管理更为复杂,可能会存在多次释放同一空间的情况

 

实验任务4

源代码task4

 1 #pragma once
 2 
 3 #include <string>
 4 
 5 class Toy
 6 {
 7 public:
 8     Toy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_);
 9 
10 public:
11     virtual ~Toy() = default;
12 
13     virtual void info();
14     virtual void touch() = 0;
15     virtual void play() = 0;
16 
17 protected:
18     std::string name;
19     double price;
20     std::string color;
21     std::string brand;
22 };
23 
24 class WoodenToy : public Toy
25 {
26 public:
27     WoodenToy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_);
28 
29     void info() override;
30     void touch() override;
31     void play() override;
32 
33 public:
34     static std::string type;
35 };
36 
37 class PlushToy : public Toy
38 {
39 public:
40     PlushToy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_);
41 
42     void info() override;
43     void touch() override;
44     void play() override;
45 
46 public:
47     static std::string type;
48 };
49 
50 class PlasticToy : public Toy
51 {
52 public:
53     PlasticToy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_);
54 
55     void info() override;
56     void touch() override;
57     void play() override;
58 
59 public:
60     static std::string type;
61 };
toy.hpp
 1 #include <iostream>
 2 #include <string>
 3 #include <iomanip>
 4 
 5 #include "toy.hpp"
 6 
 7 // Toy类实现
 8 Toy::Toy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_) : name{name_}, price{price_}, color{color_}, brand{brand_} {}
 9 
10 void Toy::info()
11 {
12     std::cout << "名称:\t" << name << std::endl;
13     std::cout << "价格:\t" << std::fixed
14               << std::setprecision(2) << price << std::endl;
15     std::cout << "颜色:\t" << color << std::endl;
16     std::cout << "品牌:\t" << brand << std::endl;
17 }
18 
19 // WoodenToy类实现
20 std::string WoodenToy::type = "木制玩具";
21 
22 WoodenToy::WoodenToy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_) : Toy{name_, price_, color_, brand_} {}
23 
24 void WoodenToy::info()
25 {
26     Toy::info();
27     std::cout << "种类:\t" << type << std::endl;
28 
29     std::cout << std::endl;
30 }
31 
32 void WoodenToy::touch()
33 {
34     std::cout << "你摸了摸木制玩具 "
35               << name << std::endl;
36 }
37 
38 void WoodenToy::play()
39 {
40     std::cout << "你使用木制玩具 " << name << " 搭建了一座城堡" << std::endl;
41 }
42 
43 // PlushToy类实现
44 std::string PlushToy::type = "毛绒玩具";
45 
46 PlushToy::PlushToy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_) : Toy{name_, price_, color_, brand_} {}
47 
48 void PlushToy::info()
49 {
50     Toy::info();
51     std::cout << "种类:\t" << type << std::endl;
52 
53     std::cout << std::endl;
54 }
55 
56 void PlushToy::touch()
57 {
58     std::cout << "你摸了摸毛绒玩具 "
59               << name << std::endl;
60 }
61 
62 void PlushToy::play()
63 {
64     std::cout << "你紧紧抱住了毛绒玩具 " << name << std::endl;
65 }
66 
67 // PlasticToy类实现
68 std::string PlasticToy::type = "塑料玩具";
69 
70 PlasticToy::PlasticToy(const std::string &name_, double price_, const std::string &color_, const std::string &brand_) : Toy{name_, price_, color_, brand_} {}
71 
72 void PlasticToy::info()
73 {
74     Toy::info();
75     std::cout << "种类:\t" << type << std::endl;
76 
77     std::cout << std::endl;
78 }
79 
80 void PlasticToy::touch()
81 {
82     std::cout << "你摸了摸塑料玩具 "
83               << name << std::endl;
84 }
85 
86 void PlasticToy::play()
87 {
88     std::cout << "塑料玩具 " << name << " 漂浮在水面上" << std::endl;
89 }
toy.cpp
 1 #pragma once
 2 
 3 #include <string>
 4 #include <vector>
 5 #include "toy.hpp"
 6 
 7 class ToyFactory
 8 {
 9 public:
10     void add(Toy* toy);
11     void show_info();
12     void use();
13 
14     ~ToyFactory();
15 
16 private:
17     std::vector<Toy *> toys;
18 };
toyfactory.hpp
 1 #include <iostream>
 2 
 3 #include "toyfactory.hpp"
 4 
 5 void ToyFactory::add(Toy *toy)
 6 {
 7     toys.push_back(toy);
 8 }
 9 
10 void ToyFactory::show_info()
11 {
12     for (Toy *t : toys)
13         t->info();
14 }
15 
16 void ToyFactory::use()
17 {
18     for (Toy *t : toys)
19     {
20         t->touch();
21         t->play();
22 
23         std::cout << std::endl;
24     }
25 }
26 
27 ToyFactory::~ToyFactory()
28 {
29     for (Toy *t : toys)
30         delete t;
31 }
toyfactory.cpp
 1 // 综合运用组合、继承、虚函数实现用一个接口尝试所有玩具特异功能
 2 
 3 #include "toy.hpp"
 4 #include "toy.cpp"
 5 #include "toyfactory.hpp"
 6 #include "toyfactory.cpp"
 7 
 8 void test()
 9 {
10     ToyFactory toyfactory;
11 
12     WoodenToy *t1 = new WoodenToy{"积木", 79.0, "Yellow", "LEGO"};
13     PlushToy *t2 = new PlushToy{"Teddy Bear", 299.99, "Brown", "jellyCat"};
14     PlasticToy *t3 = new PlasticToy{"轮船模型", 129.0, "White", "Tamiya"};
15 
16     toyfactory.add(t1);
17     toyfactory.add(t2);
18     toyfactory.add(t3);
19 
20     toyfactory.show_info();
21     toyfactory.use();
22 }
23 
24 int main()
25 {
26     test();
27     return 0;
28 }
demo4.cpp

运行测试截图

task4

问题场景描述:

这是一个玩具工厂管理系统,通过私有成员 toys 容器统一管理一组玩具对象:

玩具分为三种不同的类型:木制、毛绒、塑料;

提供公有接口访问:详细信息展示(名称、价格、颜色、品牌、种类)、使用(触摸、玩耍)

各类之间的关系及设计理由:

(1)继承:Toy 作为基类,WoodenToy、PlushToy、PlasticToy 作为它的派生类;

满足 “ is-a ”的关系,Toy 设计所有玩具类的共同属性和方法,在各派生类中多态重写

(2)组合:ToyFactory 中使用容器 toys 包含一组玩具对象指针;

满足 “ has-a ”的关系,便于统一管理工厂中所有的玩具对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/980029.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day13-影刀RPA01

今日内容 1 RPA介绍 1.1 RPA是什么 # 1 RPA(Robotic Process Automation,机器人流程自动化[自动化流程机器人])是一种通过软件机器人(或称为 “数字员工”)模拟人类在计算机上的操作行为,来自动执行重复性、规则…

11月28日总结 - 作业----

11月28日总结写机器学习作业

6001 week1

🔰 开始第 1 章:AI、机器学习、数据科学(中英文对照) (内容来自 PDF、录播1、课前预习全部相关段落)第 1 章:AI、机器学习与数据科学 Chapter 1: AI, Machine Learning, and Data Science1.1 什么是数据科学?…

TDengine IDMP “无问智推”:克服工业智能化“信息沉睡”难题的利器

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

P10055

好像和我们模拟赛T2很像啊hhh。 我们还是考虑 \(AB\) 连续段这种东西。 我们 \(2,3\) 的连续段能组成多少种长度的呢? \(2,3\) 可以,之后我们全部都用 \(2\),这样我们就可以消掉除了长度 \(>1\) 的所有连续段了。…

2025-11-28 如何更换power shell背景颜色(deepseek)

好的,更换 PowerShell 的背景颜色非常简单,主要有两种方法:一种是临时性的(仅对当前窗口有效),另一种是永久性的(通过修改配置文件)。 方法一:临时更改(通过属性设置) 这种方法最简单直观,但关闭窗口后再次…

Hikvision 考勤机数据提取(2)

import xml.etree.ElementTree as ET import requests from requests.auth import HTTPDigestAuth import json import sys import hashlib import base64 import timedef get_random():timestamp = str(int(time.ti…

P8868

询问所有区间的最大值乘积之和,这个也是好人,自然溢出取模。 考虑一次询问怎么做。我觉得从区间的角度来考虑这个东西还是蛮困难的,枚举两边的人,考虑他们两能成为几次乘积。用单调栈搞出管辖区间。首先双方的管辖…

XYD11.27模拟赛

欸,最后一场模拟赛了是吧,无话可说啊 T1: \(sum[r]^sum[r1]=sum[l-1]^sum[l1-1]\) 看到异或,想到拆位吧 考虑我们怎么快速知道上面的异或和,拆位之后我们就知道了每个二进制在中间出现了多少次? 这样是不是可以分…

P10704

对于下取整,我们有多种处理的手法。 \(\sum_{i=1}^{n}\sum_{j=1}^{n} \lfloor\frac{\lfloor{\frac{n}{a_i}\rfloor}}{a_j}\rfloor\) 开一个桶,然后本质不同的 \(a\) 只会有 \(\sqrt{1e9}\) 个。 \(\sum_{i=1}^{n}\su…

P8617

看起来很板,正好拿来练练 \(\text{SAM}\) 遍历所有节点,处理一下 \(\text{endpos}\) 集合的大小,如果 \(\ge 2\),那么就可以和答案取 \(\text{max}\) 我的 \(\text{SAM}\) 写挂了hhh,经验不足,经验不足,之后不要…

P2754

咕咕了 很快复习完字符串了,看看这个。 流量肯定是人吧,而且我觉得这个东西也很难用费用流这种东西?我靠! 二分答案是容易想到的,对于每个答案判断是否可行 直接对时间建分层图,每一层就都是 \(n\) 个点。然后建…

P2474

建个图? 使用并查集,然后搞一个DAG。 然后现在我们有了 \(A,B\) 两个点。那么小于的情况,\(A,B\)。 有一个比较暴力的做法,我们把 \(A,B\) 的所有可能取值搞出来,然后把这些取值钦定了,之后搞出其它点对钦定完和…

RAG的17种方式搭建方式研究

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

英语_阅读_Reality shows_待读

Reality shows have been very popular on TV nowadays. 现实节目如今在电视上非常受欢迎。 There are dozens of different types of programmes such as singing contests, cooking competitions or even going to l…

P3825

复习强连通分量 这个东西是三个变量欸!有点忘掉三个变量怎么处理了 这题我们有想法,也就是说,我们搞三个变量代表是否选 \(A,B,C\)。然后 \(n\) 场比赛都是这样三个点嗷。我们也可以搞两个点,代表 \(A,B\) 是否参加…

P11261

看来这题是笛卡尔树 先从区间上考虑。其实有点难考虑,我们还有一个数论分块,也就是我们首先枚举高度,那么我们要查询长度在一个区间内的管辖区间的管辖数 \(\ge a[i]\) 的区间个数。 这个感觉不是那么好做。那么我们…

P10173

三元组!考虑中间。首先我们中间的 \(\min\) 要比左边右边的 \(\max\) 大。 假如我们枚举中间那个段,那么我们找到左边所有 \(\max\) 的区间 所以肯定建立的是最小值的管辖区间吧。左边 \(\max\)的区间,我们找到前面…

HTML表格列表

信息图片关键信息/摘要‌HTML 表格‌ ‌HTML 表格‌表格由<table>标签定义,行由<tr>定义,单元格由<td>定义。基本结构包括<table>、<th>(标题栏,文字加粗)、<tr>、<td>…