Menu
Coddy logo textTech

Upcasting

Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 31 из 61.

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

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

typedef struct {
    int speed;
} Vehicle;

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

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

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

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

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

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

challenge icon

Задание

Легко

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

Преимущество upcasting заключается в том, что вы можете писать функции для базового типа, и они будут без проблем работать с любым производным типом, который его embeds. Ваш мотоцикл "is a" транспортное средство, поэтому любая функция, ожидающая Vehicle*, должна уметь его обрабатывать.

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

  • vehicle.h: Define иерархию структур с include guards. Create структуру Vehicle с двумя полями: speed (целое число для current скорости) и max_speed (целое число для максимальной скорости транспортного средства). Затем Define структуру Motorcycle, которая embeds Vehicle как свой first member и добавляет поле has_sidecar (целое число, где 1 означает «да», а 0 — «нет»). Declare две функции: boost, которая принимает Vehicle* и amount, на который нужно увеличить скорость, и print_motorcycle, которая принимает Motorcycle*.
  • vehicle.c: Implement обе функции. Ваша функция boost должна увеличить скорость транспортного средства на given amount, но ограничить её значением max_speed; если новая скорость exceed максимальную, установите вместо неё значение max_speed. Ваша функция print_motorcycle должна отображать всю information о мотоцикле, включая данные embedded транспортного средства.
  • main.c: Create объект Motorcycle и initialize все его поля. Затем продемонстрируйте upcasting, calling функцию boost: приведите Motorcycle* к типу Vehicle*, чтобы передать его в эту generic function. После увеличения скорости выведите information о мотоцикле, чтобы увидеть результат.

Вы получите четыре inputs: initial speed мотоцикла, его max speed, наличие у него бокового прицепа (1 или 0) и boost amount.

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

Speed: 75
Max Speed: 120
Sidecar: Yes

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

Главный вывод здесь заключается в том, что boost ничего не знает о мотоциклах. Она работает только с Vehicle. Однако благодаря upcasting ваш мотоцикл может использовать эту generic function для изменения данных embedded транспортного средства.

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

#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Проверьте себя

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

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

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