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.
Задание
ЛегкоДавайте создадим простую транспортную систему, демонстрирующую upcasting в действии. Вы создадите базовый тип Vehicle и более специфичный тип Motorcycle, а затем покажете, как generic function может работать с обоими типами посредством приведения указателя.
Преимущество upcasting заключается в том, что вы можете писать функции для базового типа, и они будут без проблем работать с любым производным типом, который его embeds. Ваш мотоцикл "is a" транспортное средство, поэтому любая функция, ожидающая Vehicle*, должна уметь его обрабатывать.
Вы организуете свой код в трёх файлах:
vehicle.h: Define иерархию структур с include guards. Create структуруVehicleс двумя полями:speed(целое число для current скорости) иmax_speed(целое число для максимальной скорости транспортного средства). Затем Define структуруMotorcycle, которая embedsVehicleкак свой 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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C