图形容器
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 48 课,共 61 课。
挑战
简单让 Shape Drawer 项目迎来盛大的收尾吧!你将创建一个形状容器,用来存放由圆形和矩形组成的混合集合,然后遍历它们来计算总面积,展现多态的真正威力。
在你目前完成的所有内容基础上,完整的项目将包括:
shape.h和shape.c:基础 Shape 接口和process_shape函数。circle.h和circle.c:Circle 的实现,包括构造函数和面积计算。rectangle.h和rectangle.c:Rectangle 的实现,包括构造函数和面积计算。main.c:这里就是见证奇迹的地方。你将创建一个由Shape*指针组成的数组,它可以存放任意形状类型。用各种 Circle 和 Rectangle 填充数组,然后遍历数组,计算并显示所有形状的总面积。
你的程序将接收以下输入:
- 要创建的形状数量
- 每个形状的信息:类型标识符(
c表示圆形,r表示矩形),后面跟着它的尺寸
对于容器中的每个形状,通过 Shape* 指针自行调用 draw 和 area(shapes[i]->draw(shapes[i])、shapes[i]->area(shapes[i])),并将面积累加到总数中。这里不要使用 shape.c 中的 process_shape:它会为每个形状额外输出一行 Area:,而预期输出中不包含这一行。处理完所有形状后,按照 Total Area: X.XX 的格式,以 2 位小数输出总面积。
输入示例:3,然后是 c 5.0、r 4.0 3.0、c 2.0:
Drawing Circle with radius: 5.00
Drawing Rectangle with width: 4.00 and height: 3.00
Drawing Circle with radius: 2.00
Total Area: 103.11输入示例:2,然后是 r 6.0 4.0、r 3.0 2.0:
Drawing Rectangle with width: 6.00 and height: 4.00
Drawing Rectangle with width: 3.00 and height: 2.00
Total Area: 30.00关键在于,数组存储的是 Shape* 指针。它不关心每个元素实际上是 Circle 还是 Rectangle。遍历数组并通过每个指针调用 draw 和 area 时,会根据各个形状的构造方式自动执行正确的实现。这就是多态的作用:一个循环、一个接口,多种行为。
记住,你需要将具体形状中嵌入的 Shape 成员的指针存入数组。计算圆形面积时,π 使用 3.14159。
重要提示:读取形状类型字符:使用 scanf("%d", &n) 读取形状数量后,每次读取类型字符时都必须跳过输入缓冲区中剩余的换行符。请在格式字符串开头加一个空格来实现:
scanf(" %c", &type); // note the space before %c如果不加这个前导空格,scanf 会读取上一次输入留下的换行符,而不是预期的 c 或 r,导致形状类型检查悄无声息地失败,并且不产生任何输出。
亲手试试
#include <stdio.h>
#include <stdlib.h>
#include "shape.h"
#include "circle.h"
#include "rectangle.h"
int main() {
// TODO: 读取形状的数量
// TODO: 为圆形、矩形和 Shape* 指针分配数组
// TODO: 循环遍历每个形状,读取其类型和尺寸,
// 创建形状,并将指向其基类的指针存入 shapes 数组
// TODO: 循环遍历 shapes 数组,调用 shapes[i]->draw(...) 和
// 直接调用 shapes[i]->area(...),并累加总面积
// (不要调用 process_shape:它会额外输出一行 Area)
// TODO: 按以下格式输出总面积:Total Area: X.XX
// TODO: 释放已分配的内存
return 0;
}
面向对象编程中的所有课
自己练习: C 在线编译器