Симуляция методов
Часть раздела Объектно-ориентированное программирование путешествия по 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, которая демонстрирует, как разные instances одного и того же типа структуры могут проявлять совершенно разное поведение в зависимости от того, какая function к ним подключена.
Вы создадите два файла, чтобы организовать свой код:
speaker.h: определите тип указателя наfunctionпод названиемSpeakFunc, который не принимаетparametersи ничего неreturns. Затем определите структуруSpeaker, содержащуюname(типconst char*) и указатель наspeakfunctionтипаSpeakFunc.main.c: подключите свой заголовочный файл и реализуйте три разныеfunctionдля озвучивания:bark: выводитWoof!meow: выводитMeow!chirp: выводитChirp!
Speakerinstances, каждый из которых подключён к разнойfunctionна основе входных данных, и вызовите их методыspeak, чтобы увидеть полиморфное поведение в действии.
Ваша программа получит два входных значения: тип first speaker (1 для bark, 2 для meow, 3 для chirp) и тип second speaker (также 1, 2 или 3).
Создайте два Speaker instances. Вы можете назвать их как угодно (например, "Dog", "Cat", "Bird" или просто "Speaker1", "Speaker2"). Подключите каждый из них к соответствующей function на основе его входного кода, затем вызовите speak для каждого экземпляра по порядку.
Пример вывода, если тип first равен 1, а тип second — 2:
Woof!
Meow!Пример вывода, если тип first равен 3, а тип second — 3:
Chirp!
Chirp!Главная идея заключается в том, что оба динамика имеют один и тот же тип Speaker, но при этом создают разный вывод, поскольку каждому из них назначена разная function в его члене speak. Не забудьте использовать include guards в заголовочном файле.
Попробуйте сами
#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 первого speaker на основе type1
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Привяжите функцию speak второго speaker на основе type2
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Вызовите speak у каждого speaker по порядку
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета8Полиморфизм
Указатели на функции в структурахСимуляция методовКонцепция интерфейсовРеализация интерфейсовПолиморфная итерацияПовторение: Greeter3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C