Симуляция методов
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 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*) и указатель на функциюspeakтипаSpeakFunc.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 guards в вашем заголовочном файле.
Шпаргалка
Различные экземпляры одного и того же типа структуры могут содержать разные указатели на функции, что обеспечивает полиморфное поведение.
Определите тип указателя на функцию и структуру, которая его использует:
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(); // Output: Hello!
farewell.execute(); // Output: Goodbye!
return 0;
}Оба экземпляра имеют один и тот же тип, но ведут себя по-разному при вызове их указателей на функции.
Попробуйте сами
#include <stdio.h>
#include "speaker.h"
// TODO: Реализовать функцию bark, которая выводит "Woof!"
// TODO: Реализовать функцию meow, которая выводит "Meow!"
// TODO: Реализовать функцию chirp, которая выводит "Chirp!"
int main() {
int type1, type2;
scanf("%d", &type1);
scanf("%d", &type2);
// TODO: Создать два экземпляра Speaker
// TODO: Привязать функцию speak первого динамика на основе type1
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Привязать функцию speak второго динамика на основе type2
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Вызвать speak для каждого динамика по порядку
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета8Полиморфизм
Указатели на функции в структурахСимуляция методовКонцепция интерфейсовРеализация интерфейсовПолиморфная итерацияПовторение: Greeter3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга