Menu
Coddy logo textTech

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

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

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

challenge icon

Задание

Легко

Давайте создадим систему 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;
}
quiz iconПроверьте себя

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

Все уроки раздела Object Oriented Programming