【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】

目录😋

任务描述

详细说明(类的设计)

基类: Animal

派生类:

应用程序说明:

相关知识

1. 虚函数与多态

一、多态的概念与意义

二、虚函数实现多态的原理

三、虚函数的语法细节

2. 纯虚函数与抽象类

一、纯虚函数的本质与语法规则

二、抽象类的判定与特性

三、抽象类在继承体系中的角色与意义

四、抽象类的应用场景与优势

3. 如何获取对象的类型

一、typeid 运算符概述

二、使用示例与不同编译器表现差异

三、借助 strstr 函数查看执行结果

4. 头文件说明

一、使用typeid需要包含头文件

二、strstr函数则是标准C字符串函数,需要包含头文件

三、类Animal成员name为string对象,需要包含头文件

四、调用rand()函数,需要包含头文件

编程要求

测试说明

通关代码

测试结果


任务描述

本关任务:编写一个教学游戏,通过展示每个动物的表现(move和shout),教会小朋友认识动物。程序运行后,将在动物园(至少包含Dog  Bird  Frog)中挑选出10个动物,前5个用于教学,后5个用于测试。为了保证游戏的趣味性和学习、测评质量,这10个动物是从动物园所有动物中随机挑选的。

游戏运行的示意界面如下图所示,其中下划线为小朋友的回答

详细说明(类的设计)

基类: Animal

 数据成员:

   string name; // protected成员,缺省值为类名,即Animal对象的缺省名字为Animal,Dog对象的缺少名字为Dog, 以此类推int age; // private成员,Animal对象的缺省值为0, 而对于Dog Frog 和Birds对象,它是第几个对象,age的缺省值为几。例如,声明的第1个dog对象,age为1, 第2个dog对象,age为2, 同理,第1个Bird对象,age为1,第5个Bird对象,age为5;int animalNum; // private成员,记录动物园中所有动物总数。

成员函数:

     Animal(string = "animal");//缺省构造函数void move(); // 空函数,供派生类继承void shout();// 空函数,供派生类继承string getName(); //返回nameint getAge(); // 返回age;void setName(string s); // 将s赋值给nameint getNum(); // 返回animalNum

派生类:

三个派生类Dog Frog Bird 分别 public继承 Animal,每个派生类会重新定义自己的move()和shout()

  • Dog类

   增加数据成员 :

int dogNum; // private成员,记录动物园中Dog总数

   定义成员函数:

Dog(string = "Dog");//缺省构造函数,name为Dog,age为当前Dog总数
void move();// 输出"run X feet! " (X为5+0.1*age  末尾有个空格,不回车换行)
void shout(); // 输出"wang wang, It is <名字><年龄><回车> 例:wang wang, It is Dog age 1
int getNum(); //输出当前的Dog总数
  • Frog类 与Dog类类似,增加数据成员frogNum 记录frog总数,并重新定义相关的成员函数 例如:声明的第3个Frog对象调用move和shout,将输出 jump 1.3 feet! gua gau, It is Frog age 3 其中 1.3 由1+0.1*age 计算得到
  • Bird类 与Dog类类似,增加数据成员birdNum 记录bird总数,并重新定义相关的成员函数 例如:声明的第4个Bird对象调用move和shout,将输出 fly 10.4 feet! qiu qiu, It is Bird age 4 其中 10.4 由10+0.1*age 计算得到

应用程序说明:

  通用函数,通过调用move()和shout()展示形参所指向的某种动物的行为特征:

void showAnimal(Animal *pa) 
{pa->move();pa->shout();
}

  主函数main 设计:

         假设动物园中有有10只dog,5只frog,15只bird  (此处做简化处理,更完善的程序应该允许用户自定义或按配置生成)
           从中随机挑选10只动物, 将其地址放入animalList指针数组。依次展示前5只动物(调用showAnimal())供小朋友学习。为了测试,将后5只动画的名字更改为Animal,然后依次展示后5只动物(调用showAnimal()),让小朋友根据动物表现回答是什么,答对了加20,答错不得分,最后输出得分。

相关知识

为了完成本关任务,你需要掌握:

  1. 虚函数与多态
  2. 纯虚函数与抽象类
  3. 如何获取对象的类型
  4. 所使用的头文件说明

1. 虚函数与多态

一、多态的概念与意义

       多态是面向对象编程中的一个重要特性,它使得操作接口能够呈现出多种形态。从实际应用角度来看,多态允许不同类型的对象对同一消息(函数调用)做出不同的响应,这极大地增强了程序设计的灵活性和可扩展性。

       例如,在一个图形绘制系统中,可能有多种图形类,如圆形(Circle)、矩形(Rectangle)、三角形(Triangle)等,它们都派生自一个基类图形(Shape)。而绘制这个操作对于不同的图形具体实现是不一样的,多态就能让我们使用统一的 “绘制” 接口(比如在基类中定义一个名为 draw 的虚函数),当针对不同具体图形对象调用这个接口时,能够自动执行对应图形各自正确的绘制逻辑,这样的设计使得代码的结构更加清晰、易于维护和扩展,后续如果新增一种图形类型,只需要按照统一的接口规范去实现其对应的绘制函数即可。
 

二、虚函数实现多态的原理

        在 C++ 等支持面向对象编程且有多态特性的语言中,将基类与派生类中原型相同(函数返回类型、函数名、参数列表都相同)的函数声明为虚函数,是实现多态的关键手段。

        当通过基类指针(或者引用)去调用虚函数时,程序运行时会根据指针(或引用)实际指向(或绑定)的对象的类型来决定调用的是哪个类的函数。也就是说,在编译阶段并不能确定具体调用哪个类中的函数实现,而是推迟到了运行阶段去动态判断,这也是所谓的动态绑定机制。

        对比来看,如果一个成员函数在继承树中基类和派生类多次定义,但没有声明为虚函数,那么当基类指针指向派生类对象时,调用哪个成员函数是由指针的类型决定的,而且这种调用关系在编译时就已经确定了,是一种静态绑定机制。例如:

class Base {
public:void func() {std::cout << "Base::func called" << std::endl;}
};class Derived : public Base {
public:void func() {std::cout << "Derived::func called" << std::endl;}
};int main() {Base* ptr = new Derived();ptr->func();  // 这里会输出 "Base::func called",因为没有虚函数,是静态绑定,根据指针类型 Base* 来确定调用 Base 类中的 func 函数return 0;
}

        但如果将基类中的 func 函数声明为虚函数(virtual void func() {... }),同样的代码逻辑下,ptr->func(); 就会输出 "Derived::func called",因为此时是动态绑定,根据指针实际指向的 Derived 类对象来决定调用 Derived 类中的 func 函数实现了多态的效果。
 

三、虚函数的语法细节

  虚函数的语法形式为 virtual 函数类型 函数名(形参表),具体如下:

 
  • virtual 关键字:它必须放在函数声明的最前面,用于告知编译器这个函数是虚函数,要采用动态绑定机制。比如在基类的类定义中:
    class Animal {
    public:virtual void makeSound() {std::cout << "Animal makes a sound" << std::endl;}
    };

    这里的 makeSound 函数就是虚函数,意味着后续如果有派生类重写这个函数,通过基类指针(或引用)调用该函数时会根据实际对象类型决定调用哪个类中的版本。

  • 函数类型、函数名和形参表
    • 函数类型:规定了函数返回值的类型,可以是基本数据类型(如 intdouble 等),也可以是指针、引用或者自定义的类类型等。例如:virtual int getValue(); 表示返回值类型为 int 的虚函数。
    • 函数名:遵循标识符的命名规则,要能够清晰体现函数的功能,方便代码阅读和理解,同一类中函数名不能重复(重载函数除外,重载是根据参数列表不同来区分的)。
    • 形参表:列出函数接受的参数的类型和参数名(参数名可省略,如果只是声明函数时,在函数实现时再写具体参数名),形参表决定了函数的参数特征,在派生类重写虚函数时,形参表必须和基类中对应的虚函数形参表完全一致(包括参数类型、个数、顺序等),否则就不是重写(而是函数重载了),无法实现多态效果。

  派生类中重写基类的虚函数时,除了形参表要一致外,还有一些其他注意事项:

  • 函数的 const 修饰符也要保持一致,如果基类虚函数是 const 成员函数(如 virtual void display() const {... }),那么派生类重写的函数也得是 const 成员函数。
  • 函数的返回类型在满足一定条件下可以有协变类型(即返回类型可以不同,但要满足是指针或者引用类型,并且派生类返回的指针(或引用)指向(或绑定)的类型是基类返回的指针(或引用)指向(或绑定)类型的派生类),不过一般简单的情况还是返回类型相同居多。

  正确使用虚函数的语法并遵循重写的规则,才能有效地利用多态机制来设计出灵活且易于维护的面向对象程序。

2. 纯虚函数与抽象类

一、纯虚函数的本质与语法规则

        纯虚函数是一种特殊的虚函数,其语法形式为 virtual 返回值类型 函数名 (函数参数) = 0;。这种独特的语法结构表明该函数没有具体的函数体实现,它主要是为派生类提供一个统一的接口规范,起到一种 “占位” 和 “约束” 的作用。例如,在一个图形处理库中,定义一个基类 Shape,其中有一个纯虚函数 draw,表示绘制图形的操作,但由于不同形状的图形绘制方式各异,在基类中无法给出通用的实现,于是声明为纯虚函数:

class Shape {
public:virtual void draw() = 0;  // 纯虚函数声明
};
二、抽象类的判定与特性

        当一个类包含一个或多个纯虚函数时,这个类就成为了抽象类。抽象类的关键特性在于它不是一个完整的、可直接实例化的类,它更像是一个蓝图或者框架,专门用于被其他派生类继承并扩展。以 Shape 类为例,因为它含有纯虚函数 draw,所以它是抽象类,不能像普通类那样创建对象,如 Shape s; 这样的代码是错误的,会在编译阶段被禁止。
 

三、抽象类在继承体系中的角色与意义

        抽象类作为基类,在面向对象的继承体系中具有极其重要的地位。它为派生类建立了一套通用的接口标准,强制派生类必须对这些纯虚函数进行重新定义,以实现各自特定的行为。比如,从 Shape 类派生 Circle 类和 Rectangle 类时,Circle 类和 Rectangle 类都需要重新定义 draw 函数来实现圆形和矩形的具体绘制逻辑:

class Circle : public Shape {
public:void draw() override {// 实现绘制圆形的代码}
};class Rectangle : public Shape {
public:void draw() override {// 实现绘制矩形的代码}
};

        如果派生类没有重新定义纯虚函数,那么该派生类依然是抽象类,不能被实例化,并且在编译时会报错。这一机制确保了在继承体系中,从抽象基类派生的具体类都具备符合预期的行为和功能,保证了代码的规范性和一致性,使得整个程序结构更加清晰、易于维护和扩展。例如,如果有一个 Triangle 类派生自 Shape 类,但忘记定义 draw 函数:

class Triangle : public Shape {// 未定义 draw 函数
};

        在尝试实例化 Triangle 类对象时,如 Triangle t;,编译过程就会报错,提示 Triangle 类是抽象类,因为它没有实现从基类继承的纯虚函数 draw
 

四、抽象类的应用场景与优势

        在大型软件项目中,抽象类被广泛应用。例如,在游戏开发中,各种游戏角色可能都继承自一个抽象的 Character 类,该类定义了纯虚函数 moveattack 等,不同类型的角色(如战士、法师、刺客等)在继承 Character 类后,必须根据自身特点实现这些纯虚函数,从而实现各自独特的移动和攻击方式。这种设计模式使得游戏角色的管理和扩展变得极为方便,新的角色类型只需遵循 Character 类设定的接口规范进行开发即可轻松融入游戏体系。

        又如,在图形用户界面(GUI)编程中,各种窗口组件(如按钮、文本框、列表框等)可能都派生自一个抽象的 Widget 类,该类包含纯虚函数 drawhandleEvent 等。不同的组件通过重写这些纯虚函数来实现自身的绘制和事件处理逻辑,这样的设计使得 GUI 框架具有高度的可扩展性和灵活性,能够方便地添加新的组件类型以满足不断变化的用户需求。

3. 如何获取对象的类型

一、typeid 运算符概述

        在 C++ 编程语言中,typeid 运算符扮演着十分重要的角色,它与 sizeof () 运算符类似,都是语言内置的、能够在编译或运行阶段提供特定信息的机制。typeid 运算符的核心作用在于获取对象或者类型的类型信息,其运算结果会返回一个 typeinfo 类型的对象。这个 typeinfo 类是 C++ 标准库中定义好的一个类,它封装了与类型相关的诸多细节信息,而其中比较常用的就是成员函数 name (),通过调用这个函数能够获取到对应的类型名字,进而帮助我们知晓具体的对象类型。

二、使用示例与不同编译器表现差异

        例如,我们定义一个简单的类 Dog,并且创建一个该类的对象 d,像下面这样:

class Dog {// 这里可以定义 Dog 类的成员变量、成员函数等相关内容
};Dog d;
std::string typeName = typeid(d).name();
std::cout << typeName << std::endl;

        按照常规理解,我们期望 typeid(d).name() 的值为 "Dog",但实际情况是不同的编译器在执行这一操作时,其返回结果会略有不同。这是因为 C++ 标准并没有严格规定 typeinfo::name() 函数返回的具体字符串格式,只是要求其返回结果一定包含类型名相关的信息。所以,不同的编译器厂商会根据自身的实现方式来确定具体的返回内容。

 

        比如,在某些编译器(如 GNU C++ 编译器)下,返回的字符串可能不仅仅是简单的类名,可能还会附带一些其他的标识信息,像是 N3DogE 这样的形式(这里只是示例,实际格式因版本等因素可能有变化),其中的 Dog 就是我们定义的类名,而前面和后面的字符则是编译器添加的额外标识。而在另一些编译器(如 Visual C++ 编译器)中,返回的格式又可能是其他样子。
 

三、借助 strstr 函数查看执行结果
        鉴于不同编译器返回的  typeinfo::name() 结果存在差异,在实际代码编写中,如果我们想要确切地知道某个对象的类型是否符合预期,常常会使用标准库中的  strstr 函数来辅助查看执行结果中是否包含指定的名字。 strstr 函数的功能是在一个字符串中查找另一个子字符串首次出现的位置,如果找到了,就返回指向该子字符串在原字符串中起始位置的指针;如果没找到,则返回空指针。
 

以下是一个简单的示例代码,用于演示如何结合 strstr 函数来判断对象类型是否符合期望:

#include <iostream>
#include <string>
#include <cstring>  // 包含 strstr 函数所在的头文件class Dog {// 类的定义内容
};class Cat {// 类的定义内容
};int main() {Dog d;Cat c;std::string dogTypeName = typeid(d).name();std::string catTypeName = typeid(c).name();// 检查 dogTypeName 是否包含 "Dog"if (strstr(dogTypeName.c_str(), "Dog")!= nullptr) {std::cout << "对象 d 的类型包含 Dog" << std::endl;} else {std::cout << "对象 d 的类型不包含 Dog" << std::endl;}// 检查 catTypeName 是否包含 "Cat"if (strstr(catTypeName.c_str(), "Cat")!= nullptr) {std::cout << "对象 c 的类型包含 Cat" << std::endl;} else {std::cout << "对象 c 的类型不包含 Cat" << std::endl;}return 0;
}

        在上述代码中,我们分别创建了 Dog 类和 Cat 类的对象,然后获取它们对应的类型名字符串,再通过 strstr 函数去检查字符串中是否包含相应的类名关键字,以此来判断对象的类型是否符合我们的预期。

        这种方式在处理一些复杂的、涉及多态和继承场景下的类型判断时尤为有用。例如,在存在继承关系的类层次结构中,通过基类指针指向派生类对象时,我们可以利用 typeid 结合 strstr 来准确判断指针实际指向的对象具体属于哪一个派生类,进而执行相应的、针对该派生类特性的操作逻辑,确保程序行为的正确性和灵活性。

        虽然 typeid 运算符在获取对象类型信息方面提供了便利,但由于编译器实现的差异,我们需要采用一些合适的辅助手段(如 strstr 函数)来更精准地处理和利用其返回结果,以满足实际编程中的类型判断需求。

4. 头文件说明

一、使用typeid需要包含头文件<typeinfo>

        在 C++ 编程中,当我们想要使用 typeid 运算符来获取对象或者类型的相关类型信息时,必须要包含 <typeinfo> 头文件。这个头文件中定义了 type_info 类以及和类型信息操作相关的各种声明与定义。

   type_info 类作为核心,其对象就是 typeid 运算符运算后返回的结果类型。例如,当我们写下像 typeid(someObject).name() 这样的代码去获取某个对象 someObject 的类型名称时,编译器需要知道 typeid 这个运算符如何工作以及 type_info 类中 name 函数等成员的具体实现细节,而这一切的基础就是引入了 <typeinfo> 头文件。

        如果在代码中没有包含这个头文件就使用 typeid 运算符,编译器会报错提示找不到相应的符号或者类型定义,导致编译无法顺利进行。而且,<typeinfo> 头文件不仅仅服务于简单的类型判断场景,在一些复杂的多态应用、模板编程中涉及到类型比较或者类型特征判断时,也发挥着不可或缺的作用,确保程序能够准确地知晓并处理不同对象所对应的类型情况。
 

二、strstr函数则是标准C字符串函数,需要包含头文件<cstring>

   strstr 函数是标准 C 语言中用于处理字符串的函数,在 C++ 编程里同样可以使用,不过需要包含 <cstring> 头文件。这个头文件中包含了诸多和字符串操作相关的函数声明,像字符串复制函数 strcpy、字符串拼接函数 strcat、字符串比较函数 strcmp 等等,strstr 只是其中之一。

   strstr 函数的功能是在一个给定的字符串(通常称为主字符串)中查找另一个指定的字符串(通常称为子字符串)首次出现的位置。其函数原型大致如下:

char* strstr(const char* haystack, const char* needle);
        其中, haystack 表示主字符串, needle 表示要查找的子字符串。函数返回值是一个指向主字符串中首次出现子字符串位置的指针,如果在主字符串中没有找到对应的子字符串,那么返回空指针  nullptr(在 C 语言中返回  NULL)。

        当我们在代码中需要利用 strstr 函数去判断某个字符串中是否包含特定的内容,比如前面提到的结合 typeid 获取的类型名字符串来判断是否包含期望的类名等情况时,就一定要先包含 <cstring> 头文件,否则编译器无法识别 strstr 函数,会出现编译错误,提示该函数未定义之类的问题,进而影响整个程序逻辑的实现。
 

三、类Animal成员name为string对象,需要包含头文件<string>

        在 C++ 中,如果类 Animal 的成员变量 name 被定义为 string 对象,那就必须要包含 <string> 头文件。string 类型是 C++ 标准库中提供的一个非常方便且功能强大的字符串类型,相较于传统的 C 语言风格的字符数组表示字符串(如 char str[]),它具有很多优势,比如自动管理内存、提供丰富的字符串操作方法等。

   <string> 头文件中定义了 std::string 类以及与之相关的众多成员函数和操作符重载等内容,使得我们可以像操作基本数据类型一样便捷地对字符串进行赋值、拼接、比较、获取长度等操作。例如:
 

class Animal {
public:std::string name;  // 这里定义了 name 为 string 对象,用于存储动物的名称// 可以继续定义其他 Animal 类的成员函数等内容
};Animal dog;
dog.name = "旺财";  // 利用 string 类的赋值操作符进行赋值,非常方便
std::cout << dog.name << " is a lovely dog." << std::endl;

        要是没有包含 <string> 头文件,编译器在处理涉及 std::string 类型的代码时,就不知道 std::string 是什么,无法识别相关的构造函数、成员函数等操作,从而导致编译失败,所以这个头文件对于处理 string 类型的成员变量是必不可少的。


四、调用rand()函数,需要包含头文件<stdlib>

        当在代码中调用 rand() 函数时,需要包含 <stdlib> 头文件。rand() 函数是 C/C++ 标准库中用于生成伪随机数的一个常用函数,它会返回一个范围在 0 到 RAND_MAXRAND_MAX 是一个在 <stdlib> 头文件中定义的宏常量,通常其值较大,不同系统下可能有所不同,常见值为 32767)之间的整数。

        例如,我们可以利用 rand() 函数来模拟一些随机事件或者为程序中的某些变量赋予随机初始值等情况,以下是一个简单示例:

#include <iostream>
#include <stdlib>
#include <ctime>  // 常与 <stdlib> 配合使用,用于设置随机数种子int main() {srand(static_cast<unsigned int>(time(nullptr)));  // 设置随机数种子,以获取不同的随机序列int randomNumber = rand();  // 生成一个随机数std::cout << "生成的随机数为: " << randomNumber << std::endl;return 0;
}

        在上述代码中,先是通过 srand 函数结合 time 函数(time 函数所在的 <ctime> 头文件常配合 rand 使用,用于根据当前时间设置随机数种子,使得每次运行程序生成的随机数序列不同)来初始化随机数生成器,然后调用 rand() 函数获取一个随机数并输出。
        如果没有包含 <stdlib> 头文件就去调用 rand() 函数,编译器会提示找不到该函数的定义,导致代码无法正确编译和运行,所以在涉及到需要生成随机数的编程场景中,记得要引入这个头文件来保证程序的正常运作。

编程要求

根据提示,在右侧编辑器补充完善代码,实现上述功能。

注意:已有代码已实现了部分成员函数,你可以补充完善,允许在已有代码上添加新的代码和标识(如virtual static等修饰符),但不允许删除或替换已有代码。

测试说明

平台会对你编写的代码进行测试。

注意,由于Linux和Window环境下随机数范围不同,因此在本平台的运行结果(随机挑选的动物)可能与你自己电脑中Codeblock等IDE运行结果有所不同。

测试输入:(6行, 第一行为随机数种子,后5行是小朋友猜动物的回答)

3
Frog
Frog
Bird
Dog
Dog

预期输出:

There are 30 animals in the zoo
10 dogs, 5 frogs, 15 birdsLet's study!
run 5.7 feet! wang wang, It is Dog age 7
fly 11.1 feet! qiu qiu, It is Bird age 11
fly 10.4 feet! qiu qiu, It is Bird age 4
run 5.1 feet! wang wang, It is Dog age 1
fly 10.1 feet! qiu qiu, It is Bird age 1Let's have a test!jump 1.1 feet! gua gua, It is Animal age 1
Guess! What type of animal is It?
You are right!jump 1.3 feet! gua gua, It is Animal age 3
Guess! What type of animal is It?
You are right!fly 10.2 feet! qiu qiu, It is Animal age 2
Guess! What type of animal is It?
You are right!run 5.2 feet! wang wang, It is Animal age 2
Guess! What type of animal is It?
You are right!run 5.5 feet! wang wang, It is Animal age 5
Guess! What type of animal is It?
You are right!Your score: 100

通关代码

#include <iostream>
#include <string>
#include <cstdlib>
#include <typeinfo>
using namespace std;class Animal
{
public:Animal(string name = "Animal", int age = 0) : name(name), age(age) { animalNum++; }virtual void move() {}virtual void shout() {}string getName() { return name; }void setName(string s) { name = s; }int getAge() { return age+1; }static int getNum() { return animalNum; }protected:string name;private:int age;static int animalNum;
};int Animal::animalNum = 0;class Dog : public Animal
{
public:Dog(string name = "Dog") : Animal(name, dogNum) { dogNum++; }void move() override { cout << "run " << 5 + 0.1 * getAge() << " feet! " ; }void shout() override { cout << "wang wang, It is " << getName() << " age " << getAge() << endl; }static int getNum() { return dogNum; }private:static int dogNum;
};int Dog::dogNum = 0;class Frog : public Animal
{
public:Frog(string name = "Frog") : Animal(name, frogNum) { frogNum++; }void move() override { cout << "jump " << 1 + 0.1 * getAge() << " feet! " ; }void shout() override { cout << "gua gua, It is " << getName() << " age " << getAge() << endl; }static int getNum() { return frogNum; }private:static int frogNum;
};int Frog::frogNum = 0;class Bird : public Animal
{
public:Bird(string name = "Bird") : Animal(name, birdNum) { birdNum++; }void move() override { cout << "fly " << 10 + 0.1 * getAge() << " feet! " ; }void shout() override { cout << "qiu qiu, It is " << getName() << " age " << getAge() << endl; }static int getNum() { return birdNum; }private:static int birdNum;
};int Bird::birdNum = 0;void showAnimal(Animal *pa)
{pa->move();pa->shout();
}
int main()
{Animal *animalList[10];Dog dogList[10];Frog frogList[5];Bird birdList[15];int seeds;cin >> seeds;srand(seeds);int totalnum = Animal::getNum();cout << "There are " << totalnum << " animals in the zoo" << endl;cout << Dog::getNum() << " dogs, " << Frog::getNum() << " frogs, " << Bird::getNum() << " birds\n\n";for (int i = 0; i < 10; i++){int n = rand() % (Animal::getNum());if (n < Dog::getNum())animalList[i] = &dogList[n];else if (n < Dog::getNum() + Frog::getNum())animalList[i] = &frogList[n - Dog::getNum()];elseanimalList[i] = &birdList[n - Dog::getNum() - Frog::getNum()];}cout << "Let's study!" << endl;for (int i = 0; i < 5; i++){showAnimal(animalList[i]);}for (int i = 5; i < 10; i++){animalList[i]->setName("Animal");}cout << "\nLet's have a test!" << endl << endl;int score = 0;for (int i = 5; i < 10; i++){showAnimal(animalList[i]);cout << "Guess! What type of animal is It?" << endl;string ns;cin >> ns;if ((typeid(*animalList[i]) == typeid(Dog) && ns == "Dog") ||(typeid(*animalList[i]) == typeid(Frog) && ns == "Frog") ||(typeid(*animalList[i]) == typeid(Bird) && ns == "Bird")){cout << "You are right!\n" << endl;score += 20;}else{cout << "You are wrong!\n" << endl;}}cout << "Your score: " << score << endl;return 0;
}

测试结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/503292.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我的nvim的init.lua配置

nvim的配置文件路径在&#xff5e;/.config/nvim路径下&#xff1a; 一、目录如下&#xff1a; coc-settings.json文件是配置代码片段路径的文件init.lua配置文件的启动脚本lua/config.lua 全局配置文件lua/keymaps.lua 快捷键映射键文件lua/plugins.lua 插件的安装和配置文件…

微服务-Eureka

Eureka的作用 使用RestTemplate完成远程调用需要被调用者的ip和端口&#xff0c;从而能够发起http请求&#xff0c;但是如果有很多个实例也更加不能有效的处理&#xff0c;而且我们又该如何知道这些实例是否健康呢。所以就有了很多的注册中心比如Eureka、Nacos等等。 服务注…

微服务保护—Sentinel快速入门+微服务整合 示例: 黑马商城

1.微服务保护 微服务保护是确保微服务架构可靠、稳定和安全的策略与技术。 在可靠性上&#xff0c;限流是控制进入微服务的请求数量&#xff0c;防止流量过大导致服务崩溃。比如电商促销时对商品详情服务进行流量限制。熔断是当被调用的微服务故障过多或响应过慢时&#xff0c;…

Maven 详细配置:Maven settings 配置文件的详细说明

Maven settings 配置文件是 Maven 环境的重要组成部分&#xff0c;它用于定义用户特定的配置信息和全局设置&#xff0c;例如本地仓库路径、远程仓库镜像、代理服务器以及认证信息等。settings 文件分为全局配置文件&#xff08;settings.xml&#xff09;和用户配置文件&#x…

【Uniapp-Vue3】image媒体组件属性

如果我们想要在页面上展示图片就需要使用到image标签。 这部分最重要的是图片的裁剪&#xff0c;图片的裁剪和缩放属性&#xff1a; mode 图片裁剪、缩放的模式 默认值是scaleToFill 我将用两张图片对属性进行演示&#xff0c;一张是pic1.jpg&#xff08;宽更长&#xf…

http源码分析

一、HttpURLConnection http连接池源码分析 二、HttpClient 连接池&#xff0c;每个路由最大连接数 三、OkHttp okhttp的连接池与socket连接

接口开发完后,个人对于接下来接口优化的一些思考

优化点 入参的合法性和长度范围&#xff0c;必填项的检查验证 因为没有入参&#xff0c;所以不需要考虑。 批量思想解决N1问题 // 假设要查询100个订单及其对应的用户信息 List<Order> orders orderMapper.selectList(new QueryWrapper<>().last("limit …

运动相机拍摄的视频打不开怎么办

3-10 GoPro和大疆DJI运动相机的特点&#xff0c;小巧、高清、续航长、拍摄稳定&#xff0c;很多人会在一些重要场合用来拍摄视频&#xff0c;比如可以用来拿在手里拍摄快速运动中的人等等。 但是毕竟是电子产品&#xff0c;有时候是会出点问题的&#xff0c;比如意外断电、摔重…

STM32-BKP备份寄存器RTC实时时钟

一、原理 Unix&#xff1a; 一些系统是使用32bit有符号数存储&#xff0c;实际范围为-2,147,483,648到2,147,483,647‌即~ 经过计算int32数据会在2038年1月19日溢出&#xff0c;可以看到转换的为北京时间。 STM32的时间戳为无符号时间戳。 我们需要把秒计数器的时间通过计算…

Docker图形化界面工具Portainer最佳实践

前言 安装Portainer 实践-基于Portainer安装redis-sentinel部署 Spring Boot集成Redis Sentinel 前言 本篇文章笔者推荐一个笔者最常用的docker图形化管理工具——Portainer。 安装Portainer 编写docker-compose文件 Portainer部署的步骤比较简单&#xff0c;我们还是以…

行业商机信息付费小程序系统开发方案

行业商机信息付费小程序系统&#xff0c;主要是整合优质行业资源&#xff0c;实时更新的商机信息。在当今信息爆炸的时代&#xff0c;精准、高效地获取行业商机信息对于企业和个人创业者而言至关重要。 一、使用场景 日常浏览&#xff1a;用户在工作间隙或闲暇时间&#xff0c…

基于Matlab的变压器仿真模型建模方法(13):单相升压自耦变压器的等效电路和仿真模型

1.单相升压自耦变压器的基本方程和等效电路 单相升压自耦变压器的接线原理图如图1所示。在建立自耦变压器的基本方程时,仍然把它看成是从双绕组变压器演变而来。在图1中,设节点a到节点b部分的绕组的匝数为,对应于双绕组变压器的原边绕组;节点c到节点a部分的绕组的绕组匝数为…

Java最新面试题(全网最全、最细、附答案)

一、Java基础 1、基础概念与常识Java 语言有哪些特点? 简单易学&#xff08;语法简单&#xff0c;上手容易&#xff09;&#xff1b;面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff1b;平台无关性&#xff08; Java 虚拟机实现平台无关性&a…

【简博士统计学习方法】3. 统计学习方法的三要素

3. 统计学习方法的三要素 3.1 监督学习的三要素 3.1.1 模型 假设空间&#xff08;Hypothesis Space&#xff09;&#xff1a;所有可能的条件概率分布或决策函数&#xff0c;用 F \mathcal{F} F表示。 若定义为决策函数的集合&#xff1a; F { f ∣ Y f ( X ) } \mathcal{F…

C# 修改项目类型 应用程序程序改类库

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

链上数据分析基础课:Puell倍数(Puell Multiple)

PUELL倍数&#xff08;Puell Multiple&#xff09;就是每日币发行金额除以365天的移动平均 每日币发行总额&#xff0c;简单说就是每天结算的币总量和过去一年的平均每天收成的比。这个指标能让我们大概了解矿工的收益情况&#xff0c;还能从矿工的角度看市场趋势和周期变化。 …

初学STM32 --- USMART

目录 USMART简介 USMART主要特点&#xff1a; USMART原理 USMART组成&#xff1a; USMART 的实现流程简单概括 USMART扫描函数&#xff1a; USMART系统命令 USMART移植 USMART简介 USMART是一个串口调试组件&#xff0c;可以大大提高代码调试效率&#xff01; USMART主…

对话|企业如何构建更完善的容器供应链安全防护体系

对话&#xff5c;企业如何构建更完善的容器供应链安全防护体系 云布道师 随着云计算和 DevOps 的兴起&#xff0c;容器技术和自动化成为软件开发中的必要手段&#xff0c;软件供应链也进入了自动化及 CI/CD 阶段。然而&#xff0c;容器技术和自动化虽然提升了软件的更新速度&…

Backend - EF Core(C# 操作数据库 DB)

目录 一、EF Core 1. 使用的ORM框架&#xff08;对象关系映射&#xff09; 2. EFCore 常见两种模式 3. EFCore 提供程序 二、 EF 操作数据库&#xff08;Code First&#xff09; 1. 下载NuGet插件 2.创建模型类文件 3.创建DBContext文件 4.Programs.cs文件 5.appsettings.Devel…

科研绘图系列:R语言单细胞数据常见的可视化图形

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理图1图2图3图4图5图6系统信息参考介绍 单细胞数据常见的可视化图形 因为本教程是单细胞数据,因此运行本画图脚本需要电脑的内存最少32Gb 加载…