接口概念
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 40 课,共 61 课。
到目前为止,我们的结构体都把数据和行为混在一起了,比如名称和函数指针放在一起。但如果结构体只包含函数指针呢?这样就能创建出一种强大的东西:接口。
接口定义了有哪些操作可用,但不指定它们如何工作。它是一份契约,说明“任何实现此接口的对象都必须提供这些函数。”
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;ILogger 结构体只包含一个函数指针。它本身没有任何数据,只描述行为。“I” 前缀是一种常见约定,表示这是一个接口。
任何接受 ILogger 的代码都不关心日志消息会发送到哪里。它可以输出到控制台、写入文件,或通过网络发送数据。调用方只知道可以调用 log:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // 我们不知道它具体是怎么记录日志的。
}这种分离是多态的本质。process 函数可以与任何日志记录器实现配合使用。你可以切换行为,而无需更改函数的代码。在下一课中,你将创建符合此接口契约的具体实现。
挑战
简单让我们来构建一个 ICalculator 接口:这是一个只包含函数指针的结构体,用来定义任何计算器都必须支持哪些操作,而不指定这些操作如何实现。
你需要创建两个文件来组织代码:
calculator.h:在这里定义接口。创建一个名为BinaryOp的函数指针类型,它接受两个int参数并返回一个int。然后定义一个ICalculator结构体,其中只包含两个函数指针:operate(类型为BinaryOp)和describe(一个不接受参数且不返回值的函数指针)。这个结构体纯粹是一个接口,不包含数据,只定义行为。main.c:包含头文件,并实现能够满足该接口的具体函数:add:返回两个整数的和multiply:返回两个整数的乘积describe_add:输出Additiondescribe_multiply:输出Multiplication
ICalculator实例,根据输入为它连接相应的函数,并使用它执行计算。
程序会接收 3 个输入:两个整数和一个操作代码(1 表示加法,2 表示乘法)。
根据操作代码,为 ICalculator 连接相应的一对函数。然后通过接口调用 describe 输出操作名称,接着调用 operate 进行计算并输出结果。
输入为 5、3 和 1 时的示例输出:
Addition
8输入为 4、7 和 2 时的示例输出:
Multiplication
28关键在于,ICalculator 结构体定义了一份契约:使用它的代码可以调用 describe 和 operate,而不需要知道具体连接的是哪些函数。记得在头文件中使用包含保护。
亲手试试
#include <stdio.h>
#include "calculator.h"
// TODO:实现 add 函数
// 返回两个整数的和
// TODO:实现 multiply 函数
// 返回两个整数的乘积
// TODO:实现 describe_add 函数
// 打印 "Addition"
// TODO:实现 describe_multiply 函数
// 打印 "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO:创建一个 ICalculator 实例
// TODO:根据 op_code(1 表示 add,2 表示 multiply),
// 为计算器配置相应的函数
// TODO:通过接口调用 describe
// TODO: 通过接口调用 operate,并打印结果
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器