Menu
Coddy logo textTech

Upcasting

Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 31 из 61.

Апкастинг (Upcasting) — это процесс обращения со специфическим (производным) типом как с его более общим (базовым) типом. В модели композиции языка C это означает передачу указателя на struct, которая содержит в себе базовый тип, функции, ожидающей этот базовый тип.

Рассмотрим базовую структуру Vehicle и структуру Car, которая её содержит:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Базовая структура как первый член
    int doors;
} Car;

Теперь представьте универсальную функцию, которая работает с любым транспортным средством:

void accelerate(Vehicle* v, int amount) {
    v->speed += amount;
}

Благодаря правилу первого члена, мы можем передать Car* в эту функцию, приведя его к Vehicle*:

Car my_car = {{0}, 4};  // скорость=0, двери=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed);  // Вывод: 50

Это восходящее приведение типов (upcasting) — мы перемещаемся «вверх» от конкретного типа к общему. Функция accelerate не знает, что она работает с Car; она видит только Vehicle. Это позволяет писать переиспользуемый код, который работает с базовым типом, принимая при этом любой производный тип, который его встраивает.

challenge icon

Задание

Легко

Давайте создадим простую транспортную систему, которая демонстрирует восходящее приведение типов (upcasting) в действии. Вы создадите базовый тип Vehicle и более специфичный тип Motorcycle, а затем покажете, как универсальная функция может работать с обоими типами через приведение указателей.

Сила восходящего приведения заключается в том, что вы можете писать функции для базового типа, и они будут беспрепятственно работать с любым производным типом, который его включает. Ваш мотоцикл «является» транспортным средством, поэтому любая функция, ожидающая Vehicle*, должна иметь возможность его обработать.

Вы организуете свой код в трех файлах:

  • vehicle.h: Определите иерархию структур с использованием include guards. Создайте структуру Vehicle с двумя полями: speed (целое число для текущей скорости) и max_speed (целое число для максимальной скорости транспортного средства). Затем определите структуру Motorcycle, которая включает Vehicle в качестве своего первого члена и добавляет поле has_sidecar (целое число, где 1 означает «да», а 0 — «нет»). Объявите две функции: boost, которая принимает Vehicle* и значение для увеличения скорости, и print_motorcycle, которая принимает Motorcycle*.
  • vehicle.c: Реализуйте обе функции. Ваша функция boost должна увеличивать скорость транспортного средства на заданную величину, но ограничивать ее значением max_speed — если новая скорость превысит максимальную, установите ее равной max_speed. Ваша функция print_motorcycle должна отображать всю информацию о мотоцикле, включая данные встроенного транспортного средства.
  • main.c: Создайте Motorcycle и инициализируйте все его поля. Затем продемонстрируйте восходящее приведение, вызвав функцию boost — приведите ваш Motorcycle* к Vehicle*, чтобы передать его в эту универсальную функцию. После ускорения выведите информацию о мотоцикле, чтобы увидеть результат.

Вы получите четыре входных значения: начальную скорость мотоцикла, его максимальную скорость, наличие коляски (1 или 0) и величину ускорения.

Ваш вывод должен выглядеть следующим образом:

Speed: 75
Max Speed: 120
Sidecar: Yes

Где 75 — это скорость после ускорения (начальная 50 + ускорение 25), 120 — максимальная скорость, а Yes или No указывает на наличие коляски у мотоцикла. Если ускорение превысит максимальную скорость, скорость должна быть ограничена значением max_speed.

Ключевой момент здесь заключается в том, что boost ничего не знает о мотоциклах — она работает только с Vehicle. Тем не менее, благодаря восходящему приведению, ваш мотоцикл может использовать эту универсальную функцию для изменения данных встроенного транспортного средства.

Попробуйте сами

#include <stdio.h>
#include "vehicle.h"

int main() {
    int initial_speed, max_speed, has_sidecar, boost_amount;
    
    // Считать входные данные
    scanf("%d", &initial_speed);
    scanf("%d", &max_speed);
    scanf("%d", &has_sidecar);
    scanf("%d", &boost_amount);
    
    // TODO: Создать Motorcycle и инициализировать все его поля
    // Помните: Motorcycle включает Vehicle в качестве своего первого члена
    
    // TODO: Продемонстрировать восходящее приведение типов (upcasting), вызвав boost
    // Приведите ваш Motorcycle* к Vehicle*, чтобы передать его в функцию boost
    
    // TODO: Вывести информацию о мотоцикле
    
    return 0;
}
quiz iconПроверьте себя

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

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