Встраивание структур
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 28 из 61.
В объектно-ориентированных языках, таких как C++ или Java, наследование позволяет классу приобретать свойства родительского класса. В языке C нет встроенного наследования, но мы можем имитировать его, используя технику под названием композиция — помещение одной структуры struct внутрь другой.
Идея проста: если Child нуждается во всех свойствах Parent, мы встраиваем структуру Parent как поле Child.
typedef struct {
int id;
char name[50];
} Parent;
typedef struct {
Parent parent; // Вложенная структура
int grade;
} Child;Здесь Child содержит всё, что есть у Parent, плюс своё собственное поле grade. Это композиция — дочерний объект «имеет» (has a) родителя, а не «является» (is a) родителем, но эффект аналогичен наследованию.
Чтобы получить доступ к членам родителя через экземпляр дочернего класса, используйте оператор точки дважды:
Child c;
c.parent.id = 101;
c.parent.name[0] = 'A';
c.grade = 5;Этот паттерн служит основой для создания иерархий типов в языке C. Встраивая базовую структуру struct в качестве первого члена, мы открываем доступ к мощным техникам, которые рассмотрим в следующих уроках.
Задание
ЛегкоДавайте создадим простую систему управления сотрудниками, которая демонстрирует вложение структур — основу наследования в C.
Вы создадите иерархию, в которой Employee содержит Person. Это отражает принцип работы наследования: сотрудник «является» человеком с дополнительной информацией, связанной с работой. Встраивая структуру Person внутрь Employee, вы получаете все свойства человека плюс поля, специфичные для сотрудника.
Вы организуете свой код в трех файлах:
types.h: Определите здесь обе структуры с использованием include guards. Сначала создайте структуруPersonс двумя полями:name(массив символов из 50 элементов) иage(целое число). Затем определите структуруEmployee, которая включаетPersonв качестве первого члена и добавляет полеemployee_id(целое число).types.c: Реализуйте функцию с именемprint_employee, которая принимает указатель наEmployeeи выводит информацию о нем. Обращайтесь к данным встроенного человека, используя синтаксис цепочки точек/стрелок, который вы изучили.main.c: Создайте переменную типаEmployee, заполните все ее поля (включая имя и возраст встроенного человека) и используйте функцию печати для отображения информации.
Вы получите три входных значения: имя сотрудника (строка), его возраст (целое число) и его ID сотрудника (целое число).
Ваша функция print_employee должна выводить информацию о сотруднике в следующем формате:
Employee: Alice
Age: 28
ID: 1001Где Alice — это имя, 28 — возраст, а 1001 — ID сотрудника из ваших входных данных.
Не забудьте обращаться к членам встроенной структуры через родительский член — например, чтобы добраться до имени, вам нужно сначала обратиться к члену person.
Шпаргалка
В языке C нет встроенного наследования, но его можно имитировать с помощью композиции — вложения одной структуры в другую.
Чтобы создать отношение «родитель-потомок», вложите родительскую структуру как поле в дочернюю структуру:
typedef struct {
int id;
char name[50];
} Parent;
typedef struct {
Parent parent; // Embedded struct
int grade;
} Child;Дочерняя структура содержит все свойства родительской, а также свои собственные дополнительные поля. Это представляет отношение «содержит в себе» (has a).
Для доступа к членам вложенной структуры используйте оператор «точка» дважды:
Child c;
c.parent.id = 101;
c.parent.name[0] = 'A';
c.grade = 5;При работе с указателями используйте оператор «стрелка» для доступа к членам вложенной структуры:
Child *ptr = &c;
ptr->parent.id = 101;
ptr->grade = 5;Вложение базовой структуры в качестве первого члена является распространенным паттерном, который позволяет использовать продвинутые техники для построения иерархий типов в C.
Попробуйте сами
#include <stdio.h>
#include <string.h>
#include "types.h"
int main() {
// Чтение входных данных
char name[50];
int age;
int employee_id;
fgets(name, 50, stdin);
name[strcspn(name, "\n")] = '\0'; // Удаление символа новой строки
scanf("%d", &age);
scanf("%d", &employee_id);
// TODO: Создайте переменную типа Employee
// TODO: Заполните имя и возраст вложенной структуры person
// Подсказка: Используйте strcpy для имени, доступ через employee.person.name
// TODO: Установите employee_id
// TODO: Вызовите print_employee с указателем на вашего сотрудника
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга