模拟方法
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 39 课,共 61 课。
你已经了解了如何将函数指针放入结构体中。真正强大的地方在于,你可以为同一种结构体类型的不同实例分配不同的函数。这样就能模拟行为各异的方法。
考虑一个表示具有可自定义逻辑的操作的结构体:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;创建实例时,你可以为每个实例连接不同的函数:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // 输出:Hello!
farewell.execute(); // 输出:Goodbye!
return 0;
}greet 和 farewell 的类型相同(Action),但分别调用它们的 execute 会产生完全不同的结果。这个结构体不仅保存数据,还保存了因实例而异的行为。
这种模式反映了其他语言中对象的工作方式:每个对象都可以有自己的方法实现,即使它们共享同一个接口。
挑战
简单我们来构建一个 Speaker 系统,演示同一种结构体类型的不同实例如何根据绑定的函数而表现出完全不同的行为。
你将创建两个文件来组织代码:
speaker.h:定义一个名为SpeakFunc的函数指针类型,它不接受任何参数,也不返回任何内容。然后定义一个Speaker结构体,其中包含一个name(类型为const char*)和一个类型为SpeakFunc的speak函数指针。main.c:包含你的头文件,并实现三个不同的发声函数:bark:输出Woof!meow:输出Meow!chirp:输出Chirp!
Speaker实例,根据输入为它们分别绑定不同的函数,并调用它们的speak方法,看看多态行为如何发挥作用。
程序会收到两个输入:第一个发声者类型(1 表示 bark,2 表示 meow,3 表示 chirp)和第二个发声者类型(同样是 1、2 或 3)。
创建两个 Speaker 实例。你可以随意命名它们(例如 "Dog"、"Cat"、"Bird",或者简单地命名为 "Speaker1"、"Speaker2")。根据各自的输入代码,为它们绑定相应的函数,然后按顺序调用每个实例的 speak。
第一个类型为 1、第二个类型为 2 时的输出示例:
Woof!
Meow!第一个类型为 3、第二个类型也为 3 时的输出示例:
Chirp!
Chirp!这里的关键点是,两个发声者都是同一种 Speaker 类型,但由于它们的 speak 成员分别被赋予了不同的函数,因此会产生不同的输出。记得在头文件中使用包含保护。
亲手试试
#include <stdio.h>
#include "speaker.h"
// TODO: 实现输出 "Woof!" 的 bark 函数
// TODO: 实现输出 "Meow!" 的 meow 函数
// TODO: 实现输出 "Chirp!" 的 chirp 函数
int main() {
int type1, type2;
scanf("%d", &type1);
scanf("%d", &type2);
// TODO: 创建两个 Speaker 实例
// TODO: 根据 type1 设置第一个 Speaker 的 speak 函数
// (1 = bark,2 = meow,3 = chirp)
// TODO: 根据 type2 设置第二个 Speaker 的 speak 函数
// (1 = bark,2 = meow,3 = chirp)
// TODO: 依次调用每个 Speaker 的 speak 函数
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器