实验四

实验任务一

GradeCalc.hpp

#pragma once

#include <vector>
#include <array>
#include <string>

class GradeCalc {
public:
    GradeCalc(const std::string &cname);      
    void input(int n);                         // 录入n个成绩
    void output() const;                      // 输出成绩
    void sort(bool ascending = false);        // 排序 (默认降序)
    int min() const;                          // 返回最低分(如成绩未录入,返回-1)
    int max() const;                          // 返回最高分 (如成绩未录入,返回-1)
    double average() const;                   // 返回平均分 (如成绩未录入,返回0.0)
    void info();                      // 输出课程成绩信息 

private:
    void compute();     // 成绩统计

private:
    std::string course_name;     // 课程名
    std::vector<int> grades;     // 课程成绩
    std::array<int, 5> counts;      // 保存各分数段人数([0, 60), [60, 70), [70, 80), [80, 90), [90, 100]
    std::array<double, 5> rates;    // 保存各分数段人数占比 
    bool is_dirty;      // 脏标记,记录是否成绩信息有变更
};

GradeCalc.cpp

#include <algorithm>
#include <array>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

#include "GradeCalc.hpp"

GradeCalc::GradeCalc(const std::string &cname):course_name{cname},is_dirty{true} {
    counts.fill(0);
    rates.fill(0);
}

void GradeCalc::input(int n) {
    if(n < 0) {
        std::cerr << "无效输入! 人数不能为负数\n";
        std::exit(1);
    }

    grades.reserve(n);

    int grade;

    for(int i = 0; i < n;) {
        std::cin >> grade;

        if(grade < 0 || grade > 100) {
            std::cerr << "无效输入! 分数须在[0,100]\n";
            continue;
        }

        grades.push_back(grade);
        ++i;
    }

    is_dirty = true;  // 设置脏标记:成绩信息有变更
}

void GradeCalc::output() const {
    for(auto grade: grades)
        std::cout << grade << ' ';
    std::cout << std::endl;
}

void GradeCalc::sort(bool ascending) {
    if(ascending)
        std::sort(grades.begin(), grades.end());
    else
        std::sort(grades.begin(), grades.end(), std::greater<int>());
}

int GradeCalc::min() const {
    if(grades.empty())
        return -1;

    auto it = std::min_element(grades.begin(), grades.end());
    return *it;
}

int GradeCalc::max() const {
    if(grades.empty())
        return -1;

    auto it = std::max_element(grades.begin(), grades.end());
    return *it;
}

double GradeCalc::average() const {
    if(grades.empty())
        return 0.0;

    double avg = std::accumulate(grades.begin(), grades.end(), 0.0)/grades.size();
    return avg;
}

void GradeCalc::info() {
    if(is_dirty)
       compute();

    std::cout << "课程名称:\t" << course_name << std::endl;
    std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << std::endl;
    std::cout << "最高分:\t" << max() << std::endl;
    std::cout << "最低分:\t" << min() << std::endl;

    const std::array<std::string, 5> grade_range{"[0, 60) ",
                                           "[60, 70)",
                                           "[70, 80)",
                                           "[80, 90)",
                                           "[90, 100]"};

    for(int i = static_cast<int>(grade_range.size())-1; i >= 0; --i)
        std::cout << grade_range[i] << "\t: " << counts[i] << "人\t"
                  << std::fixed << std::setprecision(2) << rates[i]*100 << "%\n";
}

void GradeCalc::compute() {
    if(grades.empty())
        return;

    counts.fill(0);
    rates.fill(0.0);

    // 统计各分数段人数
    for(auto grade:grades) {
        if(grade < 60)
            ++counts[0];        // [0, 60)
        else if (grade < 70)
            ++counts[1];        // [60, 70)
        else if (grade < 80)
            ++counts[2];        // [70, 80)
        else if (grade < 90)
            ++counts[3];        // [80, 90)
        else
            ++counts[4];        // [90, 100]
    }

    // 统计各分数段比例
    for(size_t i = 0; i < rates.size(); ++i)
        rates[i] = counts[i] * 1.0 / grades.size();

    is_dirty = false;  // 更新脏标记
}

task1.cpp

#include <iostream>
#include <string>
#include "GradeCalc.hpp"

void test() {
    GradeCalc c1("OOP");

    std::cout << "录入成绩:\n";
    c1.input(5);

    std::cout << "输出成绩:\n";
    c1.output();

    std::cout << "排序后成绩:\n";
    c1.sort(); c1.output();

    std::cout << "*************成绩统计信息*************\n";
    c1.info();

}

int main() {
    test();
}

24ea2855a6971e2a7163c8adbda2e285

#问题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容量不足时需重新分配,降低效率。

实验任务二

GradeCalc.hpp

#pragma once

#include <array>
#include <string>
#include <vector>

class GradeCalc : private std::vector<int> {
public:
    GradeCalc(const std::string& cname);
    void input(int n); // 录入n个成绩
    void output() const; // 输出成绩
    void sort(bool ascending = false); // 排序 (默认降序)
    int min() const; // 返回最低分
    int max() const; // 返回最高分
    double average() const; // 返回平均分
    void info(); // 输出成绩统计信息

private:
    void compute(); // 计算成绩统计信息

private:
    std::string course_name; // 课程名
    std::array<int, 5> counts; // 保存各分数段人数([0, 60), [60, 70), [70, 80), [80,90), [90, 100]
    std::array<double, 5> rates; // 保存各分数段占比
    bool is_dirty; // 脏标记,记录是否成绩信息有变更
};

GradeCalc.cpp

#include <algorithm>
#include <array>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

#include "GradeCalc.h"

GradeCalc::GradeCalc(const std::string& cname) : course_name{ cname }, is_dirty{ true } {
    counts.fill(0);
    rates.fill(0);
}
void GradeCalc::input(int n) {
    if (n < 0) {
        std::cerr << "无效输入! 人数不能为负数\n";
        return;
    }
    this->reserve(n);
    int grade;
    for (int i = 0; i < n;) {
        std::cin >> grade;
        if (grade < 0 || grade > 100) {
            std::cerr << "无效输入! 分数须在[0,100]\n";
            continue;
        }
        this->push_back(grade);
        ++i;
    }
    is_dirty = true;
}
void GradeCalc::output() const {
    for (auto grade : *this)
        std::cout << grade << ' ';
    std::cout << std::endl;
}
void GradeCalc::sort(bool ascending) {
    if (ascending)
        std::sort(this->begin(), this->end());
    else
        std::sort(this->begin(), this->end(), std::greater<int>());
}
int GradeCalc::min() const {
    if (this->empty())
        return -1;
    return *std::min_element(this->begin(), this->end());
}
int GradeCalc::max() const {
    if (this->empty())
        return -1;
    return *std::max_element(this->begin(), this->end());
}
double GradeCalc::average() const {
    if (this->empty())
        return 0.0;
    double avg = std::accumulate(this->begin(), this->end(), 0.0) / this->size();
    return avg;
}
void GradeCalc::info() {
    if (is_dirty)
        compute();
    std::cout << "课程名称:\t" << course_name << std::endl;
    std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() <<
        std::endl;
    std::cout << "最高分:\t" << max() << std::endl;
    std::cout << "最低分:\t" << min() << std::endl;
    const std::array<std::string, 5> grade_range{ "[0, 60) ",
    "[60, 70)",
    "[70, 80)",
    "[80, 90)",
    "[90, 100]" };
    for (int i = grade_range.size() - 1; i >= 0; --i)
        std::cout << grade_range[i] << "\t: " << counts[i] << "人\t"
        << std::fixed << std::setprecision(2) << rates[i] * 100 << "%\n";
}
void GradeCalc::compute() {
    if (this->empty())
        return;
    counts.fill(0);
    rates.fill(0);
    // 统计各分数段人数
    for (int grade : *this) {
        if (grade < 60)
            ++counts[0]; // [0, 60)
        else if (grade < 70)
            ++counts[1]; // [60, 70)
        else if (grade < 80)
            ++counts[2]; // [70, 80)
        else if (grade < 90)
            ++counts[3]; // [80, 90)
        else
            ++counts[4]; // [90, 100]
    }
    // 统计各分数段比例
    for (int i = 0; i < rates.size(); ++i)
        rates[i] = counts[i] * 1.0 / this->size();
    is_dirty = false;
}

task2.cpp

#include <iostream>
#include <string>
#include "GradeCalc.h"
void test() {
    GradeCalc c1("OOP");
    std::cout << "录入成绩:\n";
    c1.input(5);
    std::cout << "输出成绩:\n";
    c1.output();
    std::cout << "排序后成绩:\n";
    c1.sort(); c1.output();
    std::cout << "*************成绩统计信息*************\n";
    c1.info();
}
int main() {
    test();
}

e2a8cc3194ece849ed34ea5dba3168e5

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

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

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

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

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

实验任务三

Graph.hpp

#pragma once

#include <string>
#include <vector>

enum class GraphType { circle, triangle, rectangle };

class Graph
{
public:
    virtual void draw() {}
    virtual ~Graph() = default;
};

// Circle类声明
class Circle : public Graph {
public:
    void draw();
};
// Triangle类声明
class Triangle : public Graph {
public:
    void draw();
};
// Rectangle类声明
class Rectangle : public Graph {
public:
    void draw();
};


// Canvas类声明
class Canvas {
public:
    void add(const std::string& type); // 根据字符串添加图形
    void paint() const; // 使用统一接口绘制所有图形
    ~Canvas(); // 手动释放资源
private:
    std::vector<Graph*> graphs;
};

// 4. 工具函数
GraphType str_to_GraphType(const std::string& s); // 字符串转枚举类型
Graph* make_graph(const std::string& type); // 创建图形,返回堆对象指针

Graph.cpp

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>

#include "Graph.h"

// Circle类实现
void Circle::draw() { std::cout << "draw a circle...\n"; }
// Triangle类实现
void Triangle::draw() { std::cout << "draw a triangle...\n"; }
// Rectangle类实现
void Rectangle::draw() { std::cout << "draw a rectangle...\n"; }

// Canvas类实现
void Canvas::add(const std::string& type) {
    Graph* g = make_graph(type);
    if (g)
        graphs.push_back(g);
}

void Canvas::paint() const {
    for (Graph* g : graphs)
        g->draw();
}
Canvas::~Canvas() {
    for (Graph* g : graphs)
        delete g;
}

// 工具函数实现
// 字符串 → 枚举转换
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;
    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;
    default: return nullptr;
    }
}

task3.cpp

#include <string>
#include "Graph.h"

void test() {
    Canvas canvas;
    canvas.add("circle");
    canvas.add("triangle");
    canvas.add("rectangle");
    canvas.paint();
}

int main() {
    test();
}

A366026C967E34487BCE0F1509B74BCF

#问题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。

实验任务四

问题场景描述: 统一管理不同玩具​,新增玩具类型,组合电池功能

关系:继承: MusicBear,GlowRabbit公有继承Toy 

组合: Toy  包含 玩具名称,玩具类型成员

Toy.hpp

#pragma once
 #include <string>
 #include <iostream>
 #include <vector>
 // 玩具基类
 class Toy {
 public:
     Toy(const std::string& name, const std::string& type)
         : name_(name), type_(type) {}
     virtual ~Toy() = default;
     // 纯虚函数:特异功能
     virtual void specialAbility() const = 0;
     // 获取玩具信息
     std::string getName() const { return name_; }
     std::string getType() const { return type_; }
 private:
     std::string name_;   // 玩具名称
     std::string type_;   // 玩具类型
 };
 // 具体玩具:音乐毛绒熊
 class MusicBear : public Toy {
 public:
     MusicBear(const std::string& name)
         : Toy(name, "毛绒玩具-音乐熊"), volume_(5) {}
     void specialAbility() const override {
         std::cout << getName() << ":播放欢快的儿歌(音量:" << volume_ << "" << std::endl;
     }
 private:
     int volume_;  // 音量
 };
 // 具体玩具:发光兔子
 class GlowRabbit : public Toy {
 public:
     GlowRabbit(const std::string& name)
         : Toy(name, "毛绒玩具-发光兔"), brightness_(3) {}
     void specialAbility() const override {
         std::cout << getName() << ":发出柔和的彩色光(亮度:" << brightness_ << "" << std::endl;
     }
 private:
     int brightness_;  // 亮度
 };
 // 玩具工厂类(组合多个玩具)
 class ToyFactory {
 public:
     ~ToyFactory() {
         for (auto toy : toys_) {
             delete toy;
         }
     }
     // 添加玩具
     void addToy(Toy* toy) {
         if (toy) toys_.push_back(toy);
     }
     // 显示所有玩具信息
     void showAllToys() const {
         std::cout << "\n===== 玩具工厂所有玩具 =====" << std::endl;
         for (const auto& toy : toys_) {
             std::cout << "名称:" << toy->getName()
                       << " | 类型:" << toy->getType() << " | 特异功能:";
             toy->specialAbility();
         }
     }
 private:
     std::vector<Toy*> toys_;  // 组合多个玩具
};

Toy.cpp

#include "Toy.hpp" 

demo4.cpp

#include "Toy.hpp"
 void test() {
     ToyFactory factory;
     factory.addToy(new MusicBear("快乐小熊"));
     factory.addToy(new GlowRabbit("星空小兔"));
     factory.showAllToys();
 }
 int main() {
     test();
     return 0;
 }

1111111

 

posted @ 2025-12-02 22:54  王瑞琪  阅读(0)  评论(0)    收藏  举报