实验4

##实验任务1

#代码

GradeCalc.hpp

 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.cpp

  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 }

task1.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 }

##运行结果

image

 ##问题

#1.std::string course_name; 功能:存储课程名称;

std::vector<int> grades;    功能:动态存储多个学生的成绩,是统计分析的核心数据来源;

std::array<int,5> counts;    功能:固定存储5个分数段的人数,用于统计各分数段分数;

std::array<double> rates;     功能:固定存储5个分数段的人数占比,用于展示分数段分布比例;

bool is_dirty;    功能:标记成绩数据是否变更,控制compute的触发时机,避免重复计算。

#2.不合法  第2行:inupt拼写错误,应该是input;

第3行:push_back 是 std::vector<int>grades 的成员函数,而 grades 是 GradeCalc 的私有成员,外部无法直接访问,只能通过类提供的 input 接口间接添加成绩。

#3.(1)调用1次; is_dirty 作用:通过标记成绩数据grades是否发生变更,若变更,调用info时会触发compute重新统计,若未变更,则直接复用之前的统计结果,避免重复计算。

(2)不需要,理由:只需在函数内修改grades[index]后,将is_dirty 的值设为true即可,我认为也可以修改,在update_grade(index, new_grade);这行代码后面调用compute也行。

#4.直接在info函数中添加

if (!grades.empty()) {
std::vector<int> temp = grades; 
std::sort(temp.begin(), temp.end());
double a;
int size = temp.size();
if (size % 2 == 1) {
a = temp[size / 2];
} else {
a = (temp[size/2 - 1] + temp[size/2]) / 2.0;
}
std::cout << "中位数:\t" << a << std::endl;
}

5.不能;再次统计时。

6.(1)不会有影响;

(2)会。reserve()是预分配内存,没有的话,当vector容量不足时需重新分配,降低效率。

##实验任务2

#代码

GradeCalc.hpp

 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.cpp

  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.h"
 11  
 12 GradeCalc::GradeCalc(const std::string& cname) : course_name{ cname }, is_dirty{ true } {
 13     counts.fill(0);
 14     rates.fill(0);
 15 }
 16 void GradeCalc::input(int n) {
 17     if (n < 0) {
 18         std::cerr << "无效输入! 人数不能为负数\n";
 19         return;
 20     }
 21     this->reserve(n);
 22     int grade;
 23     for (int i = 0; i < n;) {
 24         std::cin >> grade;
 25         if (grade < 0 || grade > 100) {
 26             std::cerr << "无效输入! 分数须在[0,100]\n";
 27             continue;
 28         }
 29         this->push_back(grade);
 30         ++i;
 31     }
 32     is_dirty = true;
 33 }
 34 void GradeCalc::output() const {
 35     for (auto grade : *this)
 36         std::cout << grade << ' ';
 37     std::cout << std::endl;
 38 }
 39 void GradeCalc::sort(bool ascending) {
 40     if (ascending)
 41         std::sort(this->begin(), this->end());
 42     else
 43         std::sort(this->begin(), this->end(), std::greater<int>());
 44 }
 45 int GradeCalc::min() const {
 46     if (this->empty())
 47         return -1;
 48     return *std::min_element(this->begin(), this->end());
 49 }
 50 int GradeCalc::max() const {
 51     if (this->empty())
 52         return -1;
 53     return *std::max_element(this->begin(), this->end());
 54 }
 55 double GradeCalc::average() const {
 56     if (this->empty())
 57         return 0.0;
 58     double avg = std::accumulate(this->begin(), this->end(), 0.0) / this->size();
 59     return avg;
 60 }
 61 void GradeCalc::info() {
 62     if (is_dirty)
 63         compute();
 64     std::cout << "课程名称:\t" << course_name << std::endl;
 65     std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() <<
 66         std::endl;
 67     std::cout << "最高分:\t" << max() << std::endl;
 68     std::cout << "最低分:\t" << min() << std::endl;
 69     const std::array<std::string, 5> grade_range{ "[0, 60) ",
 70     "[60, 70)",
 71     "[70, 80)",
 72     "[80, 90)",
 73     "[90, 100]" };
 74     for (int i = grade_range.size() - 1; i >= 0; --i)
 75         std::cout << grade_range[i] << "\t: " << counts[i] << "人\t"
 76         << std::fixed << std::setprecision(2) << rates[i] * 100 << "%\n";
 77 }
 78 void GradeCalc::compute() {
 79     if (this->empty())
 80         return;
 81     counts.fill(0);
 82     rates.fill(0);
 83     // 统计各分数段人数
 84     for (int grade : *this) {
 85         if (grade < 60)
 86             ++counts[0]; // [0, 60)
 87         else if (grade < 70)
 88             ++counts[1]; // [60, 70)
 89         else if (grade < 80)
 90             ++counts[2]; // [70, 80)
 91         else if (grade < 90)
 92             ++counts[3]; // [80, 90)
 93         else
 94             ++counts[4]; // [90, 100]
 95     }
 96     // 统计各分数段比例
 97     for (int i = 0; i < rates.size(); ++i)
 98         rates[i] = counts[i] * 1.0 / this->size();
 99     is_dirty = false;
100 }

demo2.cpp

 1 #include <iostream>
 2 #include <string>
 3 #include "GradeCalc.h"
 4 void test() {
 5     GradeCalc c1("OOP");
 6     std::cout << "录入成绩:\n";
 7     c1.input(5);
 8     std::cout << "输出成绩:\n";
 9     c1.output();
10     std::cout << "排序后成绩:\n";
11     c1.sort(); c1.output();
12     std::cout << "*************成绩统计信息*************\n";
13     c1.info();
14 }
15 int main() {
16     test();
17 }

##运行结果

image

 ##问题

1.class GradeCalc : private std::vector<int>;

2.(1)不会(2)不能,采用私有继承。

3.继承:直接复用基类的接口

组合:通过成员变量调用其接口

4.组合;组合方式只需修改内部实现,不影响公共接口

 

##实验任务3

#代码

Graph.hpp

 1 #pragma once
 2  
 3 #include <string>
 4 #include <vector>
 5  
 6 enum class GraphType { circle, triangle, rectangle };
 7  
 8 class Graph
 9 {
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 // Triangle类声明
21 class Triangle : public Graph {
22 public:
23     void draw();
24 };
25 // Rectangle类声明
26 class Rectangle : public Graph {
27 public:
28     void draw();
29 };
30  
31  
32 // Canvas类声明
33 class Canvas {
34 public:
35     void add(const std::string& type); // 根据字符串添加图形
36     void paint() const; // 使用统一接口绘制所有图形
37     ~Canvas(); // 手动释放资源
38 private:
39     std::vector<Graph*> graphs;
40 };
41  
42 // 4. 工具函数
43 GraphType str_to_GraphType(const std::string& s); // 字符串转枚举类型
44 Graph* make_graph(const std::string& type); // 创建图形,返回堆对象指针

 

Graph.cpp

 1 #include <algorithm>
 2 #include <cctype>
 3 #include <iostream>
 4 #include <string>
 5  
 6 #include "Graph.h"
 7  
 8 // Circle类实现
 9 void Circle::draw() { std::cout << "draw a circle...\n"; }
10 // Triangle类实现
11 void Triangle::draw() { std::cout << "draw a triangle...\n"; }
12 // Rectangle类实现
13 void Rectangle::draw() { std::cout << "draw a rectangle...\n"; }
14  
15 // Canvas类实现
16 void Canvas::add(const std::string& type) {
17     Graph* g = make_graph(type);
18     if (g)
19         graphs.push_back(g);
20 }
21  
22 void Canvas::paint() const {
23     for (Graph* g : graphs)
24         g->draw();
25 }
26 Canvas::~Canvas() {
27     for (Graph* g : graphs)
28         delete g;
29 }
30  
31 // 工具函数实现
32 // 字符串 → 枚举转换
33 GraphType str_to_GraphType(const std::string& s) {
34     std::string t = s;
35     std::transform(s.begin(), s.end(), t.begin(),
36         [](unsigned char c) { return std::tolower(c); });
37     if (t == "circle")
38         return GraphType::circle;
39     if (t == "triangle")
40         return GraphType::triangle;
41     if (t == "rectangle")
42         return GraphType::rectangle;
43     return GraphType::circle; // 缺省返回
44 }
45 // 创建图形,返回堆对象指针
46 Graph* make_graph(const std::string& type) {
47     switch (str_to_GraphType(type)) {
48     case GraphType::circle: return new Circle;
49     case GraphType::triangle: return new Triangle;
50     case GraphType::rectangle: return new Rectangle;
51     default: return nullptr;
52     }
53 }

demo3.cpp

 1 #include <string>
 2 #include "Graph.h"
 3  
 4 void test() {
 5     Canvas canvas;
 6     canvas.add("circle");
 7     canvas.add("triangle");
 8     canvas.add("rectangle");
 9     canvas.paint();
10 }
11  
12 int main() {
13     test();
14 }

#运行结果

image

 ##问题

1.组合:std::vector<Graph*>graphs;功能存储多个图形对象的指针;

继承:class Circle : public Graph
           class Triangle : public Graph
           class Rectangle : public Graph

2.(1)无论g指向哪个派生类对象,都会调用基类Graph中的draw(),导致结果相同;

(2)始终调用Graph::draw(),无法调用派生类中的draw()函数;

(3)删除派生类对象时,调用基类Graph的析构函数,而不会调用派生类的析构函数,~Graph() 未声明成虚函数时无法释放派生类对象;

3. Graph.hpp类声明、enum添加  Graph.cpp类定义

4.(1)Canvas调用析构函数时;

(2)利:可以直接控制:可以精确控制内存分配和释放时机; 弊端:容易产生内存泄漏,可能对同一指针多次调用delete。

##实验任务4

#代码

toy.hpp

 1 #ifndef TOY_H
 2 #define TOY_H
 3 
 4 #include <string>
 5 #include <vector>
 6 #include <iostream> // 仅用于声明中cout的依赖
 7 
 8 // 毛绒玩具基类
 9 class Toy {
10 protected:
11     std::string name;
12     std::string type;
13     std::string color;
14     float price;
15 
16 public:
17     Toy(std::string n, std::string t, std::string c, float p);
18     virtual void specialFunction() const = 0; // 纯虚函数(无需实现)
19 
20     // 成员函数声明
21     std::string getName() const;
22     std::string getType() const;
23     std::string getColor() const;
24     float getPrice() const;
25 };
26 
27 // 会说话的玩具子类
28 class TalkingToy : public Toy {
29 private:
30     std::string voiceContent;
31 
32 public:
33     TalkingToy(std::string n, std::string t, std::string c, float p, std::string vc);
34     void specialFunction() const override;
35 };
36 
37 // 发光的玩具子类
38 class LightToy : public Toy {
39 private:
40     std::string lightColor;
41 
42 public:
43     LightToy(std::string n, std::string t, std::string c, float p, std::string lc);
44     void specialFunction() const override;
45 };
46 
47 // 玩具工厂类
48 class ToyFactory {
49 private:
50     std::string factoryName;
51     std::vector<Toy*> toyList;
52 
53 public:
54     ToyFactory(std::string fn);
55     void addToy(Toy* toy);
56     void showAllToys() const;
57     ~ToyFactory();
58 };
59 
60 #endif

Toy.cpp

 1 #include "Toy.hpp"
 2 
 3 // ========== Toy类 实现 ==========
 4 Toy::Toy(std::string n, std::string t, std::string c, float p)
 5     : name(n), type(t), color(c), price(p) {}
 6 
 7 std::string Toy::getName() const { return name; }
 8 std::string Toy::getType() const { return type; }
 9 std::string Toy::getColor() const { return color; }
10 float Toy::getPrice() const { return price; }
11 
12 
13 // ========== TalkingToy类 实现 ==========
14 TalkingToy::TalkingToy(std::string n, std::string t, std::string c, float p, std::string vc)
15     : Toy(n, t, c, p), voiceContent(vc) {}
16 
17 void TalkingToy::specialFunction() const {
18     std::cout << "" << name << "】特异功能:播放语音 -> \"" << voiceContent << "\"" << std::endl;
19 }
20 
21 
22 // ========== LightToy类 实现 ==========
23 LightToy::LightToy(std::string n, std::string t, std::string c, float p, std::string lc)
24     : Toy(n, t, c, p), lightColor(lc) {}
25 
26 void LightToy::specialFunction() const {
27     std::cout << "" << name << "】特异功能:发出" << lightColor << "色光芒" << std::endl;
28 }
29 
30 
31 // ========== ToyFactory类 实现 ==========
32 ToyFactory::ToyFactory(std::string fn) : factoryName(fn) {}
33 
34 void ToyFactory::addToy(Toy* toy) {
35     toyList.push_back(toy);
36 }
37 
38 void ToyFactory::showAllToys() const {
39     std::cout << "\n===== " << factoryName << " 玩具列表 =====" << std::endl;
40     for (const auto& toy : toyList) {
41         std::cout << "\n名称:" << toy->getName()
42                   << " | 类型:" << toy->getType()
43                   << " | 颜色:" << toy->getColor()
44                   << " | 价格:" << toy->getPrice() << "" << std::endl;
45         toy->specialFunction();
46     }
47 }
48 
49 ToyFactory::~ToyFactory() {
50     for (auto toy : toyList) {
51         delete toy;
52     }
53 }

demo4.cpp

 1 #include "Toy.hpp"
 2 #include <iostream>
 3 
 4 int main() {
 5     // 创建玩具工厂
 6     ToyFactory factory("可爱毛绒玩具工厂");
 7 
 8     // 添加不同类型的玩具
 9     factory.addToy(new TalkingToy("小维尼", "毛绒公仔", "黄色", 59.9, "你好呀,我是维尼!"));
10     factory.addToy(new LightToy("发光兔子", "夜光毛绒", "白色", 79.9, "暖白"));
11     factory.addToy(new TalkingToy("皮卡丘", "电动毛绒", "黄色", 99.9, "皮卡皮卡!"));
12     factory.addToy(new LightToy("星空独角兽", "闪光毛绒", "紫色", 89.9, "七彩渐变"));
13 
14     // 显示所有玩具信息并尝试特异功能
15     factory.showAllToys();
16 
17     return 0;
18 }

#运行结果

image

 

场景描述及各类的关系

1. 继承与虚函数:


- 基类 Toy 定义纯虚函数 specialFunction() ,作为统一接口;

- 子类 TalkingToy 、 LightToy 继承 Toy 并重写该虚函数,实现各自的特异功能。

2. 组合关系:

-  ToyFactory 类通过 vector<Toy*> 组合多个 Toy 对象,体现“工厂包含一组毛绒玩具”的设计。

3. 统一接口调用:

-  ToyFactory::showAllToys() 通过基类指针调用 specialFunction() ,利用多态性实现“一个接口尝试所有玩具特异功能”。

 

posted @ 2025-12-02 21:12  xzhls  阅读(6)  评论(0)    收藏  举报