Menu
Coddy logo textTech

Симуляция методов

Часть раздела Объектно-ориентированное программирование путешествия по 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 для каждого из них даёт совершенно разные результаты. Структура содержит не только данные. Она содержит поведение, которое различается для каждого экземпляра.

Этот шаблон отражает принцип работы объектов в других языках: у каждого объекта может быть собственная реализация метода, даже если они используют один и тот же интерфейс.

challenge icon

Задание

Легко

Давайте создадим систему Speaker, которая демонстрирует, как разные instances одного и того же типа структуры могут проявлять совершенно разное поведение в зависимости от того, какая function к ним подключена.

Вы создадите два файла, чтобы организовать свой код:

  • speaker.h: определите тип указателя на function под названием SpeakFunc, который не принимает parameters и ничего не returns. Затем определите структуру Speaker, содержащую name (тип const char*) и указатель на speak function типа SpeakFunc.
  • main.c: подключите свой заголовочный файл и реализуйте три разные function для озвучивания:
    • bark: выводит Woof!
    • meow: выводит Meow!
    • chirp: выводит Chirp!
    Затем создайте два Speaker instances, каждый из которых подключён к разной 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C