实验四

实验任务1

源代码如下:

GradeCalc.hpp

点击查看代码
#pragma once

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

class GradeCalc {
public:
    GradeCalc(const std::string &cname);      
    void input(int 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::vector<int> grades;     
    std::array<int, 5> counts;      
    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;  
}
demo1.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();
}

运行截图:

62A7C94529C55EC201B68A2F94267496

问题回答:

问题1:组合关系识别
GradeCalc 类声明中,逐行写出所有体现"组合"关系的成员声明,并用一句话说明每个被组合对象的功能。
std::vector grades;:存储课程的所有成绩数据。
std::array<int, 5> counts;:保存5个分数段的人数统计结果。
std::array<double, 5> rates;:保存5个分数段的人数占比。

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

GradeCalc c("OOP");
c.inupt(5);
c.push_back(97); // 合法吗?

不合法。grades是GradeCalc的私有成员,push_back是vector的成员函数,未通过GradeCalc的公有接口暴露,外部无法直接调用。

问题3:架构设计分析
当前设计方案中, compute 在 info 模块中调用:
(1)连续打印3次统计信息, compute 会被调用几次?标记 is_dirty 起到什么作用?
compute会被调用1次。标记is_dirty用于判断成绩数据是否发生变更,仅在数据变更时重新计算统计信息,避免重复计算提升效率。
(2)如新增 update_grade(index, new_grade) ,这种设计需要更改 compute 调用位置吗?简洁说明理由。
不需要更改。新增update_grade时,只需在函数内设置is_dirty = true,info函数会自动检测并调用compute。

问题4:功能扩展设计
要增加"中位数"统计,不新增数据成员怎么做?在哪个函数里加?写出伪代码。
在average函数旁新增median函数,利用已有grades数据计算。
伪代码:
double GradeCalc::median() const {
如果成绩列表(grades)为空,返回 0.0
std::vector temp = grades;
std::sort(temp.begin(), temp.end());
int size = temp.size();
如果 size 是奇, 返回 temp[size / 2]
否则 中间左元素 = temp[size/2 - 1]
中间右元素 = temp[size/2]
返回 (中间左元素 + 中间右元素) / 2.0

问题5:数据状态管理
GradeCalc 和 compute 中都包含代码: counts.fill(0); rates.fill(0); 。
compute 中能否去掉这两行?如去掉,在哪种使用场景下会引发统计错误?
不能去掉。若去掉,当成绩数据变更时,counts和rates会保留上一次的统计结果,导致新统计结果与历史数据叠加,出现计算错误。

问题6:内存管理理解
input 模块中代码 grades.reserve(n); 如果去掉:
(1)对程序功能有影响吗?(去掉重新编译、运行,观察功能是否受影响)
对程序功能无影响,成绩仍能正常录入、统计和输出。
(2)对性能有影响吗?如有影响,用一句话陈述具体影响。
有影响。当录入大量成绩时,vector会频繁触发内存重新分配和数据拷贝,降低程序运行效率。

实验任务2

源代码如下:

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);                        
    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;   
    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";
        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 = 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(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(size_t i = 0; i < rates.size(); ++i)
        rates[i] = counts[i] * 1.0 / this->size();
    
    is_dirty = false;
}
demo2.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();
}

运行截图:

37C2F4951A47665A6D53934E28981D45

问题回答:

问题1:继承关系识别
写出 GradeCalc 类声明体现"继承"关系的完整代码行。
class GradeCalc: private std::vector

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

GradeCalc c("OOP");
c.input(5);
c.push_back(97); // 合法吗?

不会自动成为 GradeCalc 的接口。不能编译通过,因为采用私有继承,基类 vector 的 public 接口在派生类中变为 private,外部无法直接调用push_back。

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

// 组合方式
for(auto grade: grades) // 通过什么接口访问数据
// 略
// 继承方式
for(int grade: *this) // 通过什么接口访问数据
// 略

组合方式:通过私有成员变量grades直接访问,接口由GradeCalc明确提供,封装性强。
继承方式:通过*this派生类对象本身访问,可直接使用基类的成员函数,封装性较弱。

问题4:组合 vs. 继承方案选择
你认为组合方案和继承方案,哪个更适合成绩计算这个问题场景?简洁陈述你的结论和理由。
组合方案更适合。成绩计算器与成绩存储是 “has-a” 关系,组合方式封装性更强,可灵活控制暴露的接口,且后续扩展时不会受基类接口变更的影响。

实验任务3

源代码如下:

Graph.hpp

点击查看代码
#pragma once

#include <string>
#include <vector>

enum class GraphType {circle, triangle, rectangle};

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

class Circle : public Graph {
public:
    void draw();
};

class Triangle : public Graph {
public:
    void draw();
};

class Rectangle : public Graph {
public:
    void draw();
};

class Canvas {
public:
    void add(const std::string& type); 
    void paint() const;                  
    ~Canvas();                          

private:
    std::vector<Graph*> graphs;          
};

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.hpp"

void Circle::draw()     { std::cout << "draw a circle...\n"; }

void Triangle::draw()   { std::cout << "draw a triangle...\n"; }

void Rectangle::draw()  { std::cout << "draw a rectangle...\n"; }

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;
    }
}
demo3.cpp
点击查看代码
#include <string>
#include "Graph.hpp"

void test() {
    Canvas canvas;

    canvas.add("circle");
    canvas.add("triangle");
    canvas.add("rectangle");
    canvas.paint();
}

int main() {
    test();
}

运行截图:

F5F83654712BEF9183068DA2888B6EFF

问题回答:

问题1:对象关系识别
(1)写出Graph.hpp中体现"组合"关系的成员声明代码行,并用一句话说明被组合对象的功能。
std::vector<Graph*> graphs;
存储多个图形对象的指针,支持批量添加和绘制图形。
(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 ,会出现什么问题?
会发生对象切片,vector 中仅存储基类部分,派生类的独有数据和行为丢失,无法实现多态绘制。
(3)若 ~Graph() 未声明成虚函数,会带来什么问题?
析构时仅调用基类Graph的析构函数,派生类对象的堆内存无法释放,导致内存泄漏。

问题3:扩展性思考
若要新增星形 Star ,需在哪些文件做哪些改动?逐一列出。
Graph.hpp:新增Star类声明,继承Graph并声明draw函数;更新GraphType枚举添加star。
Graph.cpp:实现Star::draw函数;修改str_to_GraphType支持 "star" 字符串转换;修改make_graph函数添加case GraphType:: star: return new Star;。

问题4:资源管理
观察 make_graph 函数和 Canvas 析构函数:
(1)make_graph 返回的对象在什么地方被释放?
在Canvas类的析构函数中被释放,通过遍历graphs调用delete释放每个图形对象。
(2)使用原始指针管理内存有何利弊?
利:直接控制内存分配和释放,灵活高效;
弊:容易遗漏释放导致内存泄漏,且手动管理成本高,易出错。

实验任务4

源代码如下:

Toy.hpp

点击查看代码
#pragma once
#include <string>
#include <vector>

// 玩具类型枚举
enum class ToyType { Singing, Glowing, StoryTelling };

// 抽象玩具基类
class Toy {
public:
    Toy(const std::string& name, ToyType type);
    virtual ~Toy() = default;
    std::string getName() const;
    ToyType getType() const;
    virtual std::string getFeature() const = 0; // 特异功能描述
    virtual void activateFeature() const = 0; // 激活特异功能

protected:
    std::string name; // 玩具名称
    ToyType type; // 玩具类型
};

// 唱歌玩具类
class SingingToy : public Toy {
public:
    SingingToy(const std::string& name, const std::string& song);
    std::string getFeature() const override;
    void activateFeature() const override;

private:
    std::string song; // 可演唱的歌曲
};

// 发光玩具类
class GlowingToy : public Toy {
public:
    GlowingToy(const std::string& name, const std::string& color);
    std::string getFeature() const override;
    void activateFeature() const override;

private:
    std::string color; // 发光颜色
};

// 讲故事玩具类
class StoryToy : public Toy {
public:
    StoryToy(const std::string& name, const std::string& story);
    std::string getFeature() const override;
    void activateFeature() const override;

private:
    std::string story; // 可讲述的故事
};

// 玩具工厂类
class ToyFactory {
public:
    ~ToyFactory();
    void addToy(Toy* toy); // 添加玩具
    void showAllToys() const; // 展示所有玩具信息
    void activateAllFeatures() const; // 激活所有玩具的特异功能
    // 按数字选择玩具并执行操作
    void selectToyByNumber(); 
    //单独查看某个玩具详情
    void showSingleToy(size_t index) const;
    // 删除指定索引的玩具
    void deleteToy(size_t index);

private:
    std::vector<Toy*> toys; // 组合多个玩具对象
    // 辅助函数:校验输入的编号是否合法
    bool isValidIndex(int input) const;
};

// 工具函数:ToyType转字符串
std::string toyTypeToString(ToyType type);
// 工具函数:清理输入缓冲区
void clearInputBuffer();
Toy.cpp
点击查看代码
#include <iostream>
#include <string>
#include <limits> 
#include "Toy.hpp"

// Toy基类实现
Toy::Toy(const std::string& name, ToyType type) : name(name), type(type) {}

std::string Toy::getName() const {
    return name;
}

ToyType Toy::getType() const {
    return type;
}

// 唱歌玩具实现
SingingToy::SingingToy(const std::string& name, const std::string& song)
    : Toy(name, ToyType::Singing), song(song) {}

std::string SingingToy::getFeature() const {
    return "演唱歌曲:《" + song + "》";
}

void SingingToy::activateFeature() const {
    std::cout << "【" << name << "】开始唱歌:" << song << "~\n";
}

// 发光玩具实现
GlowingToy::GlowingToy(const std::string& name, const std::string& color)
    : Toy(name, ToyType::Glowing), color(color) {}

std::string GlowingToy::getFeature() const {
    return "发出" + color + "色光芒";
}

void GlowingToy::activateFeature() const {
    std::cout << "【" << name << "】亮起" << color << "色灯光,一闪一闪~\n";
}

// 讲故事玩具实现
StoryToy::StoryToy(const std::string& name, const std::string& story)
    : Toy(name, ToyType::StoryTelling), story(story) {}

std::string StoryToy::getFeature() const {
    return "讲述故事:《" + story + "》";
}

void StoryToy::activateFeature() const {
    std::cout << "【" << name << "】开始讲故事:" << story << "\n";
}

// 玩具工厂实现:析构函数
ToyFactory::~ToyFactory() {
    for (Toy* toy : toys)
        delete toy; // 释放玩具对象内存
}

// 玩具工厂:添加玩具
void ToyFactory::addToy(Toy* toy) {
    if (toy)
        toys.push_back(toy);
}

// 玩具工厂:展示所有玩具信息
void ToyFactory::showAllToys() const {
    if (toys.empty()) {
        std::cout << "玩具工厂为空,暂无玩具!\n";
        return;
    }
    std::cout << "======= 玩具工厂所有玩具(共" << toys.size() << "个) =======\n";
    for (size_t i = 0; i < toys.size(); ++i) {
        std::cout << i + 1 << ". 名称:" << toys[i]->getName()
                  << " | 类型:" << toyTypeToString(toys[i]->getType())
                  << " | 特异功能:" << toys[i]->getFeature() << "\n";
    }
    std::cout << "====================================\n";
}

// 玩具工厂:激活所有玩具特异功能
void ToyFactory::activateAllFeatures() const {
    if (toys.empty()) {
        std::cout << "玩具工厂为空,无法激活功能!\n";
        return;
    }
    std::cout << "\n======= 激活所有玩具特异功能 =======\n";
    for (Toy* toy : toys) {
        toy->activateFeature();
    }
    std::cout << "====================================\n";
}

// 辅助函数:清理输入缓冲区
void clearInputBuffer() {
    std::cin.clear(); // 重置输入状态
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 忽略缓冲区所有字符
}

// 玩具工厂:校验输入的编号是否合法
bool ToyFactory::isValidIndex(int input) const {
    if (std::cin.fail()) { // 输入非数字
        clearInputBuffer();
        std::cout << "输入错误!请输入数字。\n";
        return false;
    }
    if (input < 1 || input > static_cast<int>(toys.size())) { // 数字超出范围
        std::cout << "编号无效!请输入1~" << toys.size() << "之间的数字。\n";
        return false;
    }
    return true;
}

// 玩具工厂:展示单个玩具的详细信息
void ToyFactory::showSingleToy(size_t index) const {
    if (index >= toys.size()) return;
    Toy* toy = toys[index];
    std::cout << "\n======= 玩具详情 =======\n";
    std::cout << "编号:" << index + 1 << "\n";
    std::cout << "名称:" << toy->getName() << "\n";
    std::cout << "类型:" << toyTypeToString(toy->getType()) << "\n";
    std::cout << "特异功能:" << toy->getFeature() << "\n";
    std::cout << "=======================\n";
}

// 玩具工厂:删除指定编号的玩具
void ToyFactory::deleteToy(size_t index) {
    if (index >= toys.size()) return;
    delete toys[index]; // 释放玩具对象内存
    toys.erase(toys.begin() + index); // 从向量中移除指针
    std::cout << "已成功删除编号" << index + 1 << "的玩具!\n";
    // 删除后更新列表
    if (!toys.empty()) {
        std::cout << "\n更新后的玩具列表:\n";
        showAllToys();
    } else {
        std::cout << "\n玩具工厂已无玩具!\n";
    }
}

// 玩具工厂:按数字选择玩具并执行操作
void ToyFactory::selectToyByNumber() {
    if (toys.empty()) {
        std::cout << "玩具工厂为空,无法选择玩具!\n";
        return;
    }

    int toyNum;
    // 1. 输入玩具编号并校验
    do {
        std::cout << "\n请输入要操作的玩具编号(1~" << toys.size() << "):";
        std::cin >> toyNum;
    } while (!isValidIndex(toyNum));

    size_t selectedIndex = toyNum - 1; // 转换为向量索引(0开始)
    char choice;

    // 2. 单个玩具操作菜单
    do {
        std::cout << "\n======= 玩具操作菜单(当前选择:" << toys[selectedIndex]->getName() << ")=======\n";
        std::cout << "1. 查看该玩具详情\n";
        std::cout << "2. 激活该玩具特异功能\n";
        std::cout << "3. 删除该玩具\n";
        std::cout << "4. 退出菜单\n";
        std::cout << "请输入操作编号(1~4):";
        std::cin >> choice;
        clearInputBuffer(); // 清理输入缓冲区

        //执行选择的操作
        switch (choice) {
            case '1':
                showSingleToy(selectedIndex);
                break;
            case '2':
                std::cout << "\n======= 激活玩具功能 =======\n";
                toys[selectedIndex]->activateFeature();
                std::cout << "==========================\n";
                break;
            case '3':
                deleteToy(selectedIndex);
                return; // 删除后退出当前选择流程
            case '4':
                std::cout << "已返回上一级!\n";
                break;
            default:
                std::cout << "操作无效!请输入1~4之间的数字。\n";
        }
    } while (choice != '4'); // 输入4时退出菜单
}

// 工具函数:ToyType转字符串
std::string toyTypeToString(ToyType type) {
    switch (type) {
        case ToyType::Singing: return "唱歌玩具";
        case ToyType::Glowing: return "发光玩具";
        case ToyType::StoryTelling: return "讲故事玩具";
        default: return "未知类型";
    }
}
demo.cpp
点击查看代码
#include <iostream>
#include "Toy.hpp"

// 显示主菜单
void showMainMenu() {
    std::cout << "\n======= 电子毛绒玩具工厂系统 =======\n";
    std::cout << "1. 查看所有玩具列表\n";
    std::cout << "2. 选择单个玩具操作(按编号)\n";
    std::cout << "3. 激活所有玩具特异功能\n";
    std::cout << "4. 退出系统\n";
    std::cout << "====================================\n";
}

int main() {
    ToyFactory factory;
    // 初始化玩具工厂(添加3个示例玩具)
    factory.addToy(new SingingToy("音乐小熊", "小星星"));
    factory.addToy(new GlowingToy("荧光小兔", "粉紫"));
    factory.addToy(new StoryToy("智慧小鹿", "三只小猪"));
    showMainMenu();
    char mainChoice;
    // 主循环
    do {
    	std::cout << "请输入操作编号(1~4):";
        std::cin >> mainChoice;
        clearInputBuffer(); // 清理输入缓冲区
        // 执行主菜单操作
        switch (mainChoice) {
            case '1':
                factory.showAllToys();
                break;
            case '2':
                factory.selectToyByNumber();
                break;
            case '3':
                factory.activateAllFeatures();
                break;
            case '4':
                std::cout << "已退出系统,感谢使用!\n";
                break;
            default:
                std::cout << "操作无效!请输入1~4之间的数字。\n";
        }
    } while (mainChoice != '4'); // 输入4时退出系统

    return 0;
}

运行截图:

DD8B89654AF94EB7ECCD693B30F453A1

问题描述:

设计电子毛绒玩具系统,包含多种具有特异功能的玩具(如唱歌、发光、讲故事),通过玩具工厂类管理所有玩具,支持统一展示玩具信息和调用特异功能。

对象关系:

继承:Toy为抽象基类,SingingToy、GlowingToy、StoryToy为派生类,体现 “is-a” 关系。
组合:ToyFactory包含vector<Toy*>成员,管理多个玩具对象,体现 “has-a” 关系。

posted @ 2025-12-02 16:34  OSCR  阅读(2)  评论(0)    收藏  举报