实验4

task 1

GradeCalc.h

 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);
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::vector<int> grades;
24     std::array<int, 5> counts;
25     std::array<double, 5> rates;
26     bool is_dirty;
27 };
GradeCalc.h

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 
 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)","[60,70)","[70,80)","[80,90)","[90,100]" };
 89 
 90     for (int i = grade_range.size() - 1;i >= 0;--i)
 91         std::cout << grade_range[i] << "\t:" << counts[i] << "人\t" << std::fixed << std::setprecision(2) << rates[i] * 100 << "%\n";
 92 }
 93 
 94 void GradeCalc::compute() {
 95     if (grades.empty())
 96         return;
 97 
 98     counts.fill(0);
 99     rates.fill(0);
100 
101     for (auto grade : grades) {
102         if (grade < 60)
103             ++counts[0];
104         else if (grade < 70)
105             ++counts[1];
106         else if (grade < 80)
107             ++counts[2];
108         else if (grade < 90)
109             ++counts[3];
110         else
111             ++counts[4];
112     }
113 
114     for (int i = 0;i < rates.size();++i)
115         rates[i] = counts[i] * 1.0 / grades.size();
116 
117     is_dirty = false;
118 }
GradeCalc.cpp

demo1.cpp

 1 #include<iostream>
 2 #include<string>
 3 #include"GradeCalc.h"
 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 int main() {
22     test();
23 }
demo1.cpp

image

 Q1:

std::string course_name:存储课程名称,字符串对象被GradeCale包含。

std::vector<int> grades:动态存储成绩,支持成绩增删与遍历,动态数组对象被GradeCalc包含。

std::array<int,5> counts:统计各个分段的人数,固定大小数组对象被GradeCalc包含。

std::arrar<double,5> rates:储存各个分数段人员的占比,固定大小数组对象被GradeCalc包含。

Q2:不合法。std::vector<int> grades是GradeCalc中的私有成员,外部的push_back无法调用。

Q3:

(1)compute被调用1次,只有第一次is_dirty为true的时候被调用;is_dirty用于标识成绩是否发生变化,当成绩被修改时,is_dirty为true,否则为false。

(2)不需要。update_grade(index, new_grade)的功能时变更成绩,改变is_dirty的值,compute只是通过is_dirty判断要不要调用。

Q4:

 1 double GradeCalc::middle() const {
 2     if (grades.empty()) {
 3         return 0; 
 4     }
 5 
 6     std::vector<int> t_grades = grades;
 7     std::sort(t_grades.begin(), t_grades.end()); 
 8 
 9     int size = t_grades.size();
10     if (size % 2 == 1) 
11         return t_grades[size / 2];
12     else
13         return (t_grades[size / 2 - 1] + t_grades[size / 2]) / 2;
14 }

Q5:不能去掉。若第一次调用input输入成绩,然后再调用info,此时is_dirty为true,需要调用compute,counts和rates就有了各自数组对应的值;第二次调用input新增成绩,再调用info,因为新增了数据is_dirty还是true,调用compute,此时counts和rates没有被置零,还是保留了第一次的数据和第二次的新数据叠加,会得出错误数据。

Q6:

(1)没有影响。grades.reverse(n)的作用是提前为vector分配n个元素的内存空间。

(2)有影响。如果n较大时,没有提前分配内存空间,会增加数据分配和拷贝的开销。

 

 

task 2

GradeCalc.h

 1 #pragma once
 2 
 3 #include<vector>
 4 #include<array>
 5 #include<string>
 6 
 7 class GradeCalc:private std::vector<int>{
 8 public:
 9     GradeCalc(const std::string& cname);
10     void input(int 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;
24     std::array<double, 5> rates;
25     bool is_dirty;
26 };
GradeCalc.h

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 
 17 void GradeCalc::input(int n) {
 18     if (n < 0) {
 19         std::cerr << "无效输入! 人数不能为负数\n";
 20         std::exit(1);
 21     }
 22 
 23     this->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         this->push_back(grade);
 36         ++i;
 37     }
 38 
 39     is_dirty = true;
 40 }
 41 
 42 void GradeCalc::output() const {
 43     for (auto grade : *this)
 44         std::cout << grade << ' ';
 45     std::cout << std::endl;
 46 }
 47 
 48 void GradeCalc::sort(bool ascending) {
 49     if (ascending)
 50         std::sort(this->begin(), this->end());
 51     else
 52         std::sort(this->begin(), this->end(), std::greater<int>());
 53 }
 54 
 55 int GradeCalc::min() const {
 56     if (this->empty())
 57         return -1;
 58 
 59     auto it = std::min_element(this->begin(), this->end());
 60     return *it;
 61 }
 62 
 63 int GradeCalc::max() const {
 64     if (this->empty())
 65         return -1;
 66 
 67     auto it = std::max_element(this->begin(), this->end());
 68     return *it;
 69 }
 70 
 71 double GradeCalc::average() const {
 72     if (this->empty())
 73         return 0.0;
 74 
 75     double avg = std::accumulate(this->begin(), this->end(), 0.0) / this->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)","[60,70)","[70,80)","[80,90)","[90,100]" };
 89 
 90     for (int i = grade_range.size() - 1;i >= 0;--i)
 91         std::cout << grade_range[i] << "\t:" << counts[i] << "人\t" << std::fixed << std::setprecision(2) << rates[i] * 100 << "%\n";
 92 }
 93 
 94 void GradeCalc::compute() {
 95     if (this->empty())
 96         return;
 97 
 98     counts.fill(0);
 99     rates.fill(0);
100 
101     for (auto grade : *this) {
102         if (grade < 60)
103             ++counts[0];
104         else if (grade < 70)
105             ++counts[1];
106         else if (grade < 80)
107             ++counts[2];
108         else if (grade < 90)
109             ++counts[3];
110         else
111             ++counts[4];
112     }
113 
114     for (int i = 0;i < rates.size();++i)
115         rates[i] = counts[i] * 1.0 / this->size();
116 
117     is_dirty = false;
118 }
GradeCalc.cpp

demo2.cpp

 1 #include<iostream>
 2 #include<string>
 3 #include"GradeCalc.h"
 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 int main() {
22     test();
23 }
demo2.cpp

image

 Q1:class GradeCalc:private std::vector<int>

Q2:不会自动成为接口,代码编译无法通过。push.back是vector的公有成员,而基类vector是私有继承,仅在GradeCalc内部可以访问,test作为外部代码无法访问。

Q3:

组合方式:通过类的私有成员变量grades访问数据。仅类内部的接口可以直接访问,外部代码无法直接访问。

继承方式:通过派生类自身对象访问数据。私有继承下基类接口仅内部可以使用,外部不可用。

Q4:组合方式更适。成绩计算主要功能是管理与统计成绩数据,组合方式将vector作为成绩存储组件,耦合度低、封装性更优,通过接口的修改便于控制成绩的访问逻辑,也可以根据需求替换存储容器;而私有继承派生类与vector强绑定,耦合度高,容易因继承特性隐含接口暴露风险。

 

 

task 3

Graph.h

 1 #pragma once
 2 
 3 #include<string>
 4 #include<vector>
 5 
 6 enum class GraphType{circle,triangle,rectangle};
 7 
 8 class Graph{
 9 public:
10     virtual void draw() {};
11     virtual ~Graph() {};
12 };
13 
14 class Circle :public Graph {
15 public:
16     void draw();
17 };
18 
19 class Triangle :public Graph {
20 public:
21     void draw();
22 };
23 
24 class Rectangle :public Graph {
25 public:
26     void draw();
27 };
28 
29 class Canvas {
30 public:
31     void add(const std::string& type);
32     void paint() const;
33     ~Canvas();
34 
35 private:
36     std::vector<Graph*> graphs;
37 };
38 
39 GraphType str_to_GraphType(const std::string& s);
40 Graph* make_graph(const std::string& type);
Graph.h

Graph.cpp

 1 #include<algorithm>
 2 #include<cctype>
 3 #include<iostream>
 4 #include<string>
 5 
 6 #include "Graph.h"
 7 
 8 void Circle::draw() { std::cout << "draw a circle...\n"; }
 9 
10 void Triangle::draw() { std::cout << "draw a triangle...\n"; }
11 
12 void Rectangle::draw() { std::cout << "draw a rectangle...\n"; }
13 
14 void Canvas::add(const std::string& type) {
15     Graph* g = make_graph(type);
16     if (g)
17         graphs.push_back(g);
18 }
19 void Canvas::paint() const {
20     for (Graph* g : graphs)
21         g->draw();
22 }
23 
24 Canvas::~Canvas() {
25     for (Graph* g : graphs)
26         delete g;
27 }
28 
29 GraphType str_to_GraphType(const std::string& s) {
30     std::string t = s;
31     std::transform(s.begin(), s.end(), t.begin(), [](unsigned char c) {return std::tolower(c);});
32 
33     if (t == "circle")
34         return GraphType::circle;
35 
36     if (t == "triangle")
37         return GraphType::triangle;
38 
39     if (t == "rectangle")
40         return GraphType::rectangle;
41 
42     return GraphType::circle;
43 }
44 
45 Graph* make_graph(const std::string& type) {
46     switch (str_to_GraphType(type)) {
47     case GraphType::circle: return new Circle;
48     case GraphType::triangle : return new Triangle;
49     case GraphType::rectangle: return new Rectangle;
50     default:return nullptr;
51     }
52 }
Graph.cpp

demo3.cpp

 1 #include<string>
 2 #include"Graph.h"
 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 }
demo3.cpp

image

 Q1:

(1)组合关系:std::vector<Graph*> graphs;用于动态存储和管理所有图形对象的指针。

(2)继承关系:class Circle:public Graph;  class Triangle:public Graph;  class Rectangle:public Graph;

Q2:

(1)若未声明则会运行失败。g->draw()会调用基类Graph的draw()函数,最终没有任何输出。

(2)若失去指针,vector<Graph>存储的式Graph类的实体对象,无法存储派生类对象(Circle、Triangle和Rectangle),最终g->draw()只能调用基类的空实现,无法绘制不同图形。

(3)若未声明成虚函数,delete g会调用基类的析构函数不会调用派生类的析构函数,使资源无法释放,导致内存泄露。

Q3:

enum class GraphType{circle , triangle , rectangle , star };

 

class Star: public Graph{

public:

   void draw();

}

 

str_to_GraphType辅助函数中添加:if( t == "star" ) return GraphType:: star;

make_graph辅助函数中(switch下)添加:case GraphType::star:  return new Star;

Q4:

(1)make_graph的返回对象在Canvas的析构函数中被释放。

(2)优点:基类指针可以指向任意派生类,并且可以触发多态调用;原始指针可以手动控制对象的创建和释放,集中管理对象的生命周期。

缺点:需要手动匹配new与delete,如果忘记将导致内存泄漏。

 

 

task 4

Toy.h

 1 #pragma once
 2 #include<string>
 3 #include<vector>
 4 
 5 enum class ToyType{edog,robot,care_rabbit};
 6 
 7 class Toy {
 8 public:
 9     Toy(const std::string& name,const std::string& type,double price);
10     virtual void info() ;
11     virtual ~Toy() = default;
12 
13 private:
14     std::string NAME;
15     std::string TYPE;
16     double PRICE;
17 };
18 
19 class Edog :public Toy {
20 public:
21     Edog(const std::string& name, const std::string& type, double price);
22     void info();
23 };
24 
25 class Robot :public Toy {
26 public:
27     Robot(const std::string& name, const  std::string& type, double price);
28     void info();
29 };
30 
31 class Care_rabbit :public Toy {
32 public:
33     Care_rabbit(const std::string& name, const std::string& type, double price);
34     void info();
35 };
36 
37 class ToyFactory {
38 public:
39     void add(const std::string &name,const std::string& type,double price);
40     void showinfo() const;
41     ~ToyFactory();
42 
43 private:
44     std::vector<Toy*> toys;
45 };
46 
47 ToyType str_to_ToyType(const std::string& s);
48 Toy* make_toy(const std::string& name, const std::string& type, double price);

Toy.cpp

 1 #include "Toy.h"
 2 #include<iostream>
 3 #include<string>
 4 #include<cctype>
 5 #include<algorithm>
 6 
 7 Toy::Toy(const std::string& name, const std::string& type, double price) :NAME{ name }, TYPE{ type }, PRICE{ price }{}
 8 
 9 void Toy::info() {
10     std::cout << "名字:" << NAME << std::endl;
11     std::cout << "类型:" << TYPE << std::endl;
12     std::cout << "价格:" << PRICE << std::endl;
13 }
14 
15 Edog::Edog(const std::string& name, const std::string& type, double price):Toy(name,type,price) {}
16 
17 void Edog::info() {
18     Toy::info();
19     std::cout << "特异功能:摇头、摆尾,并发出“汪汪”的叫声" << std::endl;
20     std::cout<<std::endl;
21 }
22 
23 Robot::Robot(const std::string& name, const std::string& type, double price) :Toy(name, type, price) {}
24 
25 void Robot::info() {
26     Toy::info();
27     std::cout << "特异功能:与它对话发,它发出语音:“你好,你想问我什么问题呢?”" << std::endl;
28     std::cout << std::endl;
29 }
30 
31 Care_rabbit::Care_rabbit(const std::string& name, const std::string& type, double price) :Toy(name, type, price) {}
32 
33 void Care_rabbit::info() {
34     Toy::info();
35     std::cout << "特异功能:放出轻柔的音乐,陪伴入睡" << std::endl;
36     std::cout << std::endl;
37 }
38 
39 void ToyFactory::add(const std::string& name, const std::string& type, double price) {
40     Toy* g = make_toy(name,type,price);
41     if (g)
42         toys.push_back(g);
43 }
44 
45 void ToyFactory::showinfo() const {
46     for (Toy* g : toys)
47         g->info();
48 }
49 
50 ToyFactory::~ToyFactory() {
51     for (Toy* g : toys)
52         delete g;
53 }
54 
55 ToyType str_to_ToyType(const std::string& s) {
56     std::string t = s;
57     std::transform(s.begin(), s.end(), t.begin(),[](unsigned char c) { return std::tolower(c);});
58 
59     if (t == "edog")
60         return ToyType::edog;
61 
62     if (t == "robot")
63         return ToyType::robot;
64 
65     if (t == "care_rabbit")
66         return ToyType::care_rabbit;
67 
68     return ToyType::edog;
69 }
70 
71 Toy* make_toy(const std::string &name,const std::string& type,double price) {
72     std::string t_name = name;
73     std::string t_type = type;
74 
75     switch (str_to_ToyType(type)) {
76     case ToyType::edog: return new Edog(t_name,t_type,price);
77     case ToyType::robot: return new Robot(t_name, t_type, price);
78     case ToyType::care_rabbit: return new Care_rabbit(t_name, t_type, price);
79     default: return nullptr;
80     }
81 }

demo4.cpp

 1 #include"Toy.h"
 2 #include<iostream>
 3 
 4 void test() {
 5     ToyFactory toyfactory;
 6 
 7     toyfactory.add("puppy", "edog", 120.0);
 8     toyfactory.add("罗伯特", "robot", 398.8);
 9     toyfactory.add("兔兔", "care_rabbit", 199.9);
10     toyfactory.showinfo();
11 }
12 
13 int main() {
14     test();
15 }

这个代码里有不同类型的电子毛绒玩具(电子狗、智能机器人、陪伴兔),通过统一接口展示所有玩具的基础信息:名称、类型、价格和专属特异功能。

继承关系:Edog/Robot/Care_rabbit都是派生类,基类是Toy。

组合关系:ToyFactory是整体,std::vector<Toy*> toys是部分。

posted @ 2025-12-02 23:15  璐Luzi  阅读(2)  评论(0)    收藏  举报