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. Это позволяет писать переиспользуемый код, который работает с базовым типом, принимая при этом любой производный тип, который его встраивает.
Задание
ЛегкоДавайте создадим простую транспортную систему, которая демонстрирует восходящее приведение типов (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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигур