Menu
CoddyTech

项目概览

本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 44 课,共 61 课。

challenge icon

挑战

简单

让我们先创建基础的 Shape 结构体,为 Shape Drawer 项目打好基础:它是一个接口,通过函数指针定义任何形状都能执行的操作。

你将创建两个文件来搭建这个多态系统:

  • shape.h:在这里定义核心接口。首先,为 Shape 结构体创建前向声明。然后定义两种函数指针类型:
    • DrawFunc:接收一个 const Shape* 参数,不返回任何值
    • AreaFunc:接收一个 const Shape* 参数,并返回一个 double
    最后,定义 Shape 结构体本身,其中包含两个函数指针:draw(类型为 DrawFunc)和 area(类型为 AreaFunc)。这个结构体只是一个接口。它不包含任何特定形状的数据,只包含行为。
  • main.c:创建一个简单的测试形状,演示你的接口可以正常工作。实现两个函数:
    • test_draw:打印 Drawing test shape
    • test_area:返回 42.0
    创建一个 Shape 实例,将它连接到这两个测试函数,然后通过接口调用这两个函数。将面积结果以一位小数的形式打印出来。

输出应展示函数指针可以正常工作:

Drawing test shape
Area: 42.0

为什么需要前向声明?你可能会想,为什么 typedef struct Shape Shape; 会出现在结构体完整定义的前面。这是因为 DrawFunc 和 AreaFunc 的参数类型中引用了 const Shape*,但在文件的那个位置,编译器还没有看到完整的结构体定义。前向声明告诉编译器:“存在一个名为 Shape 的结构体”,这就足以让你使用指向它的指针。完整定义会在后面出现。只要结构体需要引用自身(直接引用或通过函数指针引用),这种写法在 C 中就很常见。

这里的关键在于,Shape 并不了解圆形或矩形。它只知道任何形状都必须能够 draw 自身并计算自己的 area。在接下来的课中,你将创建具体形状,让它们包含这个基础结构体,并提供各自的实现。记得在头文件中使用包含保护。

亲手试试

#include <stdio.h>
#include "shape.h"

// TODO: 实现 test_draw 函数
// - 接收一个 const Shape* 参数
// - 打印 "Drawing test shape"
void test_draw(const Shape* shape) {
    // TODO: 打印要求的消息
}

// TODO: 实现 test_area 函数
// - 接收一个 const Shape* 参数
// - 返回 42.0
double test_area(const Shape* shape) {
    // TODO: 返回测试用的面积值
}

int main() {
    // TODO: 创建一个 Shape 实例
    
    // TODO: 将 shape 连接到 test_draw 和 test_area 函数
    
    // TODO: 通过接口调用 draw
    
    // TODO: 通过接口调用 area,并将结果保留一位小数后打印
    // 格式:"Area: %.1f\n"
    
    return 0;
}

面向对象编程中的所有课

自己练习: C 在线编译器