结构体中的函数指针
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 38 课,共 61 课。
到目前为止,我们一直将函数指针用作独立变量,或将它们作为参数传递。迈向多态的下一步,是将函数指针直接嵌入结构体中,让结构体拥有自己的行为。
当结构体包含函数指针时,每个实例都可以保存不同的函数。这意味着,调用函数指针时,两个相同类型的对象可以表现不同:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // 作为成员的函数指针
} Printer;Printer 结构体现在有两个成员:数据(name)和行为(print)。要使用它,你需要在初始化时将一个函数赋值给指针:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // 输出:!!! Hello !!!
return 0;
}注意,我们是通过结构体调用函数的:p.print("Hello")。这看起来很像在其他语言中调用对象的方法。结构体同时包含数据和操作这些数据的函数。
这种模式是 C 语言中多态的基础:不同的实例可以被分配不同的函数,因此同一种结构体类型可以表现出不同的行为。
挑战
简单让我们构建一个 Notifier 系统,演示结构体如何通过函数指针携带自己的行为。
你将创建两个文件来组织代码:
notifier.h:定义一个名为NotifyFunc的函数指针类型,它接受一个const char*参数且不返回任何值。然后定义一个Notifier结构体,其中包含一个name(类型为const char*)和一个类型为NotifyFunc的notify函数指针。main.c:包含你的头文件,并实现两个通知函数:alert_notify:按以下格式输出消息:[ALERT] messageinfo_notify:按以下格式输出消息:[INFO] message
Notifier实例,根据输入设置相应的函数,并通过结构体的函数指针调用通知函数。
程序会接收两个输入:一个通知类型(1 表示警报,2 表示信息)和一条要显示的消息。
根据类型,将对应的函数赋给通知器的 notify 成员,然后通过结构体调用它来显示消息。
类型为 1,消息为 "System starting" 时的示例输出:
[ALERT] System starting类型为 2,消息为 "All systems normal" 时的示例输出:
[INFO] All systems normal记得在头文件中使用包含保护,并通过结构体成员调用函数:n.notify(message)
亲手试试
#include <stdio.h>
#include "notifier.h"
// TODO:实现 alert_notify 函数
// 应输出:[ALERT] message
// TODO:实现 info_notify 函数
// 应输出:[INFO] message
int main() {
int type;
char message[256];
// 读取输入
scanf("%d", &type);
getchar(); // 读取换行符
fgets(message, sizeof(message), stdin);
// 如果 message 末尾有换行符,就将其删除
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO:创建一个 Notifier 实例
// TODO:根据 type(1 表示 alert,2 表示 info),
// 将相应的函数赋给 notifier 的 notify 成员
// TODO:通过结构体的函数指针调用通知函数
// 使用:n.notify(message)
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器