实验内容

利用C++语言,参考教材,实现Shape程序开发与测试,包括各种图形对象的定义,在主函数中实现图形对象的添加、删除操作。具体技术内容包括

  • 派生层次设计
  • 虚函数定义及纯虚函数的定义
  • 虚析构函数的定义
  • 结合虚函数理解多态的概念
  • 给出实验结果截图及实验分析

实验原理

派生

系统提供了相当多的已定义好的基类,用户可以根据具体的应用,在已有类的基础上构架新类,在派生类中,可以直接使用父类部分的代码,但却不需要重新编写父类的代码。这样可以加速软件开发的速度,保障软件开发的质量。继承提供了在已有类的基础上开发出新类的机制,可以节省重复代码的编写工作,是软件重用的基础。

派生结构图

image-20221123170143224

虚函数

虚函数,就是在基类声明函数是虚拟的,在派生类中才正式定义的函数。虚函数的作用就是在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。

在运用虚函数后,基类指针指向派生类对象,就能调用派生类的虚函数。用同一指针变量,可以调用同一类族中不同类的虚函数,也就是对同一消息,不同对象有不同的响应方式。

纯虚函数

只声明,不定义的虚函数称为纯虚函数。纯虚函数的作用是为派生类提供一个一致的接口 (界面)。它的定义留给派生类来做,派生类根据需要来定义各自的实现。有时候 可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

虚析构函数

虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。

虚函数重写

重写要求必须是虚函数且父类的虚函数必须有 virtual 关键字,函数的参数列表和返回值也必须相同。子类中重写的虚函数的访问修饰符可以不同。

多态

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

测试程序

由于太过麻烦,所以函数声明和实现都写在同一个文件里了。

Shape类

1
2
3
4
5
6
7
8
// 定义抽象基类
#include <cstring>
class Shape {
public:
virtual double area() const = 0; // 定义纯虚函数
virtual std::string name() const = 0;
// 在成员函数后面加 const ,表示常量成员函数,不修改对象里任何成员变量的值
};

Rectangle类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle : public Shape {
public:
Rectangle(double w, double h) : width(w), height(h) {}; // 定义构造函数
virtual double area() const {
return width * height;
}
virtual std::string name() const {
return "rectangle";
}

private:
double width, height;
};

Circle类

1
2
3
4
5
6
7
8
9
10
11
12
13
class Circle : public Shape {
public:
Circle(double r) : radius(r) {}; // 定义构造函数
virtual double area() const {
return 3.14159 * radius * radius;
}
virtual std::string name() const {
return "circle";
}

private:
double radius; // 圆形半径
};

Square类

1
2
3
4
5
6
7
8
9
10
11
12
13
class Square : public Shape {
public:
Square(double l) : length(l) {}; // 定义构造函数
virtual double area() const {
return length * length;
}
virtual std::string name() const {
return "square";
}

private:
double length; // 正方形边长
};

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include "shape.h"
#include "circle.h"
#include "square.h"
#include "rectangle.h"
#include <iostream>
using namespace std;
int main() {
Circle circle(4);
// 建立 Circle 类对象 circle
Square square(3.5);
// 建立 Square 类对象 square
Rectangle rectangle(2.5, 6.4);
// 建立 Rectangle 类对象 rectangle
Shape *pt[3] = {&circle, &square, &rectangle};
// 定义基类指针数组 pt,使它每一个元素指向一个派生类对象
for (int i = 0; i < 3; i++) {
std::cout << "name: " << pt[i]->name() << ", area: " << pt[i]->area() << std::endl; // 输出总面积
}

return 0;,
}

实验结果及分析

image-20221123170601357