实验任务一:
源码:
#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;//脏标记,记录是否成绩信息有变更 };
#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默认升序elsestd::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; }
#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(); }
运行测试结果截图:

回答问题:
问题1:组合关系识别
GradeCalc 类声明中,逐行写出所有体现"组合"关系的成员声明,并用一句话说明每个被组合对象的功能。
答:std::string course_name; //记录课程名称
问题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];elsereturn (sorted_grades[n / 2 - 1] + sorted_grades[n / 2 + 1]) / 2.0;//偶数个求平均 }
运行结果测试截图:(是否sort并不影响中位数正确输出)

问题5:数据状态管理
GradeCalc 和 compute 中都包含代码: counts.fill(0); rates.fill(0); 。 compute 中能否去掉这两行?如去掉,在哪种使用场景下会引发统计错误?
答:不能。如果去掉,改变已经定义的GradeCalc类型对象的成绩信息时,counts和rates并未重新置0,counts和rates会在原有的基础上增加。
问题6:内存管理理解
input 模块中代码 grades.reserve(n); 如果去掉:
(1)对程序功能有影响吗?(去掉重新编译、运行,观察功能是否受影响)
答:对程序功能无影响。

(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;//脏标记,记录是否成绩信息有变更 };
#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默认升序elsestd::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; }
#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(); }
运行结果测试截图:

回答问题:
问题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);//创建图形,返回堆对象指针
#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;//若字符串无法转成有效枚举值,返回空指针 } }
task3.cpp运行结果测试截图:

回答问题:
问题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()函数,无法实现多态。

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

(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 返回的对象在什么地方被释放?
答:所有图形绘制完后一起释放

(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);
#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;} }
#pragma once #include <vector> #include "Toy.hpp"//玩具工厂类 class ToyFactory{ public:void info() const;//显示工厂玩具信息void addToy(Toy* toy); //添加玩具到工厂~ToyFactory();private:std::vector<Toy*> Toy_info; //存储玩具指针 };
#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(); }
#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; }
运行测试结果截图:
