实验四 组合与继承

实验任务一:

源码:

#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;
    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.hpp
#include <algorithm>
#include <array>
#include <cstdlib>//包含C标准库的通用工具
#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";   //cerr(标准错误输出流)
        //与cout是两个独立的流,可通过系统分离(如将普通输出写入文件,错误输出仍显示在屏幕)
        //cerr无缓冲
        std::exit(1);
    }

    grades.reserve(n);//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());//sort默认升序
    else
        std::sort(grades.begin(),grades.end(),std::greater<int>());//greater专门用来表示“大于”关系//()表示创建一个实例
}

int GradeCalc::min() const{
    if(grades.empty())
        return -1;
    
        //min_element函数定义在<algorithm>头文件中,默认<比较
    auto it = std::min_element(grades.begin(),grades.end());
    return *it;//迭代器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::fixed来自<iomanip>,指定浮点数以固定小数点格式进行输出,通常与std::setprecision()搭配使用
    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(grades.empty())
        return;

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

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

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

    is_dirty = false;
}
GradeCalc.cpp
#include <iostream>
#include <string>
#include "GradeCalc.hpp"

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

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

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

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

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

int main(){
    test();
}
demo1.cpp

运行测试结果截图:

image

回答问题:

问题1:组合关系识别

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

答:std::string course_name;  //记录课程名称

  std::vector<int> grades; //保存成绩
  std::array<int,5> counts; //保存各分数段人数
  std::array<double,5> rates; //保存各分数段人数占比

问题2:接口暴露理解

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

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

答:不合法。push_back是std::vector类中提供的接口,可以向vector中添加数据元素。但是GradeCalc类中std::vector<int>grades是私有成员,只能在类的内部访问。而GradeCalc类中也并没有提供push_back的公有接口

问题3:架构设计分析

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

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

答:1次。is_dirty是脏标记,用来判断对象成绩信息是否变更。 

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

答:update_grade(index, new_grade) 这种设计,需要更改 compute 调用位置。当通过input和update添加或修改了成绩后is_dirty = true,compute调用位置需放函数最后。在info中,先用is_dirty来判断成绩信息是否变更,如果变更需要调用comput重新统计成绩信息,再执行输出逻辑。

问题4:功能扩展设计

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

答:不新增数据成员,可以新增成员函数median,利用数据成员里vector<int> grades通过索引找到中位数。info里添加median()输出。

代码:

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

    //中位数根据下标索引确定位置,排序前后数据会发生变化,所以需要先排序再求索引下标。
    std::vector<int> sorted_grades = grades;//新vector sorted_grades不会直接修改原grades信息
    std::sort(sorted_grades.begin(),sorted_grades.end());
    int n = sorted_grades.size();
    if(n % 2 == 1)//奇数个取中间
        return sorted_grades[n / 2];
    else
        return (sorted_grades[n / 2 - 1] + sorted_grades[n / 2 + 1]) / 2.0;//偶数个求平均
}

运行结果测试截图:(是否sort并不影响中位数正确输出)

image

问题5:数据状态管理

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

答:不能。如果去掉,改变已经定义的GradeCalc类型对象的成绩信息时,counts和rates并未重新置0,counts和rates会在原有的基础上增加。

问题6:内存管理理解

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

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

答:对程序功能无影响

image

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

答:对性能有影响。容器的底层存储一般是连续的内存块,当容器中元素数量超过内存块容量时需要扩容,而扩容会消耗大量时间,reserve能提前预留至少能容纳n个元素的内存空间而且不改变容器实际元素数量,后续添加元素时只要不超过n,就不会触发扩容,提高效率

实验任务二:

源码:

#pragma once

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

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::vector<int> grades;
    std::array<int,5> counts;//保存各分数段人数
    std::array<double,5> rates;//保存各分数段人数占比
    bool is_dirty;//脏标记,记录是否成绩信息有变更
}; 
GradeCalc.hpp
#include <algorithm>
#include <array>
#include <cstdlib>//包含C标准库的通用工具
#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";   //cerr(标准错误输出流)
        //与cout是两个独立的流,可通过系统分离(如将普通输出写入文件,错误输出仍显示在屏幕)
        //cerr无缓冲
        return;
    }

    this->reserve(n);//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());//sort默认升序
    else
        std::sort(this->begin(),this->end(),std::greater<int>());//greater专门用来表示“大于”关系//()表示创建一个实例
}

int GradeCalc::min() const{
    if(this->empty())
        return -1;
    
        //min_element函数定义在<algorithm>头文件中,默认<比较
    auto it = std::min_element(this->begin(),this->end());
    return *it;//迭代器it是指针类型
}

int GradeCalc::max() const{
    if(this->empty())
        return -1;

    auto it = std::max_element(this->begin(),this->end());
    return *it;
}

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" << median() << std::endl;
    //std::fixed来自<iomanip>,指定浮点数以固定小数点格式进行输出,通常与std::setprecision()搭配使用
    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.0);

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

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

    is_dirty = false;
}
GradeCalc.cpp
#include <iostream>
#include <string>
#include "GradeCalc.hpp"

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

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

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

    std::cout << "*************排序前成绩统计信息*************\n";
    cl.info();

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

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

int main(){
    test();
}
demo2.cpp

运行结果测试截图:

image

回答问题:

问题1:继承关系识别

写出 GradeCalc 类声明体现"继承"关系的完整代码行。

class GradeCalc:private std::vector<int>

问题2:接口暴露理解

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

答:当前private继承方式下,基类vector的接口自动成为GradeCalc的私有接口。如在test模块中使用,无法编译通过。因为GradeCalc从vector中继承过来的接口是private类型私有接口,只能在类中访问,无法在test中外部访问

问题3:数据访问差异

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

继承方式 vs 组合方式
this->push_back(grade);
 
grades.push_back(grade);

差异:GradeCalc继承的vector接口相当于它自身内部有这些接口,可以通过this指针访问接口;而组合方式中GradeCalc本身内部并没有vector所拥有的接口,只是因为类vector内嵌在其中,所以GradeCalc可以通过std::vector使用vector的接口

问题4:组合 vs. 继承方案选择

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

答:我认为组合方案更适合成绩计算这个问题场景。首先需要明确的是类GradeCalc是一个计算器类,它的核心是实现成绩的一系列计算统计和输出。而vector中的接口是用来做细节部分的成绩处理GradeCalc的核心功能与vector中的接口功能并不一致。虽然组合和继承方案都能正确得到最终结果,但从逻辑角度而言,组合方案更加清晰地展现了计算器类和vector类的内在逻辑关系

实验任务三:

源码:

#pragma once

#include <string>
#include <vector>

enum class GraphType {circle,triangle,rectangle};//枚举类enum class属于枚举类型

//Graph类定义
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;
};

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

#include "Graph.hpp"

//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();//调用每一个graphs中具体图形的draw函数
}

Canvas::~Canvas(){
    for(Graph* g :graphs)
        delete g;//释放内存
}

//工具函数实现
//字符串 -> 枚举转换//把用户的字符串指令转成程序内部能识别的枚举标记
GraphType str_to_GraphType(const std::string& s){
    std::string t = s;
    //批量处理容器元素//将s中所有字符转为小写,并把结果存到字符串t中,解决“字符串大小写不匹配”的问题
    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;//若字符串无法转成有效枚举值,返回空指针
    }
}
Graph.cpp
task3.cpp

运行结果测试截图:

image

回答问题:

问题1:对象关系识别

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

答:功能:在Canvas中存储需要绘制的图形类型

private:
    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() 运行结果会有何不同?

答:会没有输出结果,即无法绘制对应图形。因为不声明virtual,g->draw()会调用基类的draw,无法让派生类覆盖基类中的draw()函数,无法实现多态。

image

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

答:会导致图形内存无法分配,无法绘制图形。make_graph()返回的是Graph*,而vector<Graph>存放Graph类型对象,类型不匹配。创建图形的核心代码是Graph* make_graph()函数,该函数在堆内存中创建对应图形的对象,并返回该堆对象的指针。而Canvas根据返回的堆对象指针创建相应的图形,若把指针类型改为Graph类型,则无法根据指针创建相应图形。

image

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

答:无法调用派生类析构函数,只调用基类析构函数,继而无法释放派生类调用内存,造成内存泄漏

问题3:扩展性思考

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

答:Graph.hpp : 枚举类GraphType里新增Star类型 ; 新增Star类声明

  Graph.cpp : Star类实现 ; str_to_GraphType函数中新增Star字符串转换为枚举类型功能 ; make_graph函数中返回star类型的堆指针

  taskk3.cpp : test()中新增语句 canvas.add("Star");

enum class GraphType {circle,triangle,rectangle,star};
//Star类声明
class Star : public Graph{
public:
    void draw();
};
//Star类实现
void Star::draw(){
    std::cout << "draw a Star...\n";
}
if(t == "star")
        return GraphType::star;
case GraphType::star:
            return new Star;
canvas.add("Star");

问题4:资源管理

观察 make_graph 函数和 Canvas 析构函数:

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

答:所有图形绘制完后一起释放

image

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

答: 利:可以直接操作内存地址,比如return new Circle手动分配内存。

   弊:容易遗漏delete释放内存,造成内存泄漏,比如delete g手动释放内存 ;

     容易产生野指针问题,造成未定义行为,比如:若未将指针指控,后续易导致程序无法正确运行。

default:
            return  nullptr;

实验任务四:

问题场景描述:

问题需求是集中统一展示玩具工厂中所有玩具的功能。以Toy类为基类,扩展出不同功能类型的派生类,再通过ToyFactory类集中管理不同种类玩具,并提供统一接口尝试所有玩具的不同功能。

各类关系及设计理由:

Toy类为基类,封装了玩具的核心数据成员,如名称、颜色、类型、尺寸,还有通用功能接口,如语音交互、录音播放、发光、名称获取,并通过虚函数和虚析构函数保证多态。Companion、Decoration、Interaction、Portable四类具体玩具公有继承Toy类,并重写对应虚函数。ToyFactory类通过组合关系内嵌vector,统一提供玩具添加、信息展示的接口,借助多态实现不同类型玩具的统一调用。

源码:

#pragma once

#include <string>

enum class ToyType{companion,decoration,interaction,portable};//陪伴、装饰、互动、便携类型

//玩具类
class Toy{
    public:
        Toy():Toy_name("未命名玩具"){}
        Toy(const std::string& toy_name):Toy_name{toy_name}{}
        virtual void Voice_Interaction();//语音交互功能
        virtual void Play_function();//录音播放功能
        virtual void Light_up();//发光功能
        virtual ~Toy() = default;//默认虚析构函数

    public:
        virtual std::string get_name() const;
    private:
        std::string Toy_name;//玩具名称
        std::string color;//玩具颜色
        ToyType Toy_Type;//玩具类型
        int size;//玩具尺寸
};

//companion类声明
class Companion : public Toy{
    public:
        Companion() = default;
        Companion(const std::string& toy_name):Toy{toy_name}{}
        void Voice_Interaction();
        void Play_function();
        void Light_up();
        ~Companion();
        std::string get_name() const;
};

//Decoration类声明
class Decoration : public Toy{
    public:
        Decoration() = default;
        Decoration(std::string& toy_name):Toy{toy_name}{}
        void Voice_Interaction();
        void Play_function();
        void Light_up();
        ~Decoration();
        std::string get_name() const;
};

//Interaction类声明
class Interaction : public Toy{
    public:
        Interaction() = default;
        Interaction(std::string& toy_name):Toy{toy_name}{}
        void Voice_Interaction();
        void Play_function();
        void Light_up();
        ~Interaction();
        std::string get_name() const;
};

//Portable类声明
class Portable : public Toy{
    public:
        Portable() = default;
        Portable(std::string& toy_name):Toy{toy_name}{}
        void Voice_Interaction();
        void Play_function();
        void Light_up();
        ~Portable();
        std::string get_name() const;
};

//工具函数
ToyType str_to_ToyType(const std::string& s);//字符串转枚举类型
// std::string ToyType_to_str(ToyType type);      // 枚举转字符串
Toy* make_toy(const std::string& type);
Toy.hpp
#include "Toy.hpp"
#include <iostream>

//Toy类虚函数默认实现
void Toy::Voice_Interaction(){
    std::cout << "该玩具不支持语音交互功能" << std::endl;
}

void Toy::Play_function(){
    std::cout << "该玩具不支持录音播放功能" << std::endl;
}

void Toy::Light_up(){
    std::cout << "该玩具不支持发光功能" << std::endl;
}

std::string Toy::get_name() const{
    return Toy_name;
}

//Companion类实现
void Companion::Voice_Interaction(){
    std::cout << "陪伴类玩具:正在进行语音聊天互动" << std::endl;
}

void Companion::Play_function(){
    std::cout << "陪伴类玩具:播放录制的故事" << std::endl;
}

void Companion::Light_up(){
    std::cout << "陪伴类玩具:柔和灯光已亮起(助眠模式)" << std::endl;
}

Companion::~Companion(){
}

std::string Companion::get_name() const {
    return "陪伴类";
}

//Decoration类实现
void Decoration::Voice_Interaction(){
    std::cout << "装饰类玩具:不支持语音交互功能" << std::endl;
}

void Decoration::Play_function(){
    std::cout << "装饰类玩具:不支持录音播放功能" << std::endl;
}

void Decoration::Light_up(){
    std::cout << "装饰类玩具:彩色氛围灯已亮起(装饰模式)" << std::endl;
}

Decoration::~Decoration(){
}

std::string Decoration::get_name() const{
    return "装饰类";
}

//Interaction类实现
void Interaction::Voice_Interaction(){
    std::cout << "互动类玩具:正在识别语音指令" << std::endl;
}

void Interaction::Play_function(){
    std::cout << "互动类玩具:播放互动游戏的提示音" << std::endl;
}

void Interaction::Light_up(){
    std::cout << "互动类玩具:灯光配合游戏节奏闪烁" << std::endl;
}

Interaction::~Interaction(){
}

std::string Interaction::get_name() const{
    return "互动类";
}

//Portable类实现
void Portable::Voice_Interaction(){
    std::cout << "便携类玩具:简短语音对话" << std::endl;
}

void Portable::Play_function(){
    std::cout << "便携类玩具:播放简短歌曲片段" << std::endl;
}

void Portable::Light_up(){
    std::cout << "便携类玩具:指示灯亮起" << std::endl;
}

Portable::~Portable(){
}

std::string Portable::get_name() const{
    return "便携类";
}

ToyType str_to_ToyType(const std::string& s){
    if(s == "companion")
        return ToyType::companion;
    if(s == "decoration")
        return ToyType::decoration;
    if(s == "interaction")
        return ToyType::interaction;
    if(s == "portable")
        return ToyType::portable;
    else{
        std::cerr << "无效类型" << std::endl;
        exit(1);//非void类型终止程序
    }
}

Toy* make_toy(const std::string& type){
    ToyType toyType = str_to_ToyType(type);
    switch (toyType){
        case ToyType::companion:
            return new Companion();
        case ToyType::decoration:
            return new Decoration();
            case ToyType::interaction:
        return new Interaction();
        case ToyType::portable:
            return new Portable();
        default:
            return  nullptr;
    }
}
Toy.cpp
#pragma once
#include <vector>
#include "Toy.hpp"

//玩具工厂类
class ToyFactory{
public:
    void info() const;//显示工厂玩具信息
    void addToy(Toy* toy); //添加玩具到工厂
    ~ToyFactory();

private:
    std::vector<Toy*> Toy_info; //存储玩具指针
};
ToyFactory.hpp
#include "Toy.hpp"
#include "ToyFactory.hpp"
#include <iostream>

//向工厂添加玩具
void ToyFactory::addToy(Toy* toy) {
    if (toy){
        Toy_info.push_back(toy);
        std::cout << "玩具已添加到工厂" << std::endl;
    }
    else{
        std::cerr << "无法添加空玩具" << std::endl;
    }
}

//info()显示工厂所有玩具的信息
void ToyFactory::info() const{
    std::cout << "\n****************************************************" << std::endl;
    std::cout << "\t\t所有玩具功能信息" << std::endl;
    std::cout << "****************************************************\n" << std::endl;
    for (int i = 0; i < Toy_info.size(); i++) {
        std::cout << "[" << i + 1 << "]" << Toy_info[i]->get_name() << "玩具功能测试:" << std::endl;
        Toy_info[i]->Voice_Interaction();
        Toy_info[i]->Play_function();
        Toy_info[i]->Light_up();
        std::cout << "\n";
    }
    std::cout << "****************************************************\n" << std::endl;
}

ToyFactory::~ToyFactory() {
    for (auto& toy : Toy_info) {
        delete toy;
    }
    Toy_info.clear();
}
ToyFactory.cpp
#include "Toy.hpp"
#include "ToyFactory.hpp"
#include <iostream>

int main(){
        ToyFactory factory;

        factory.addToy(make_toy("companion"));
        factory.addToy(make_toy("decoration"));
        factory.addToy(make_toy("interaction"));
        factory.addToy(make_toy("portable"));

        factory.info();
        return 0;
}
demo4.cpp

运行测试结果截图:

image

 

posted @ 2025-11-29 16:35  黎明Z  阅读(5)  评论(0)    收藏  举报