Доступ к элементам родителя
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 30 из 61.
Теперь, когда мы понимаем встраивание структур и правило первого члена, давайте сосредоточимся на практической стороне: как читать и изменять данные встроенного родителя из функции.
Когда функция получает структуру Child (или указатель на неё), доступ к полям родителя требует навигации через встроенную структуру. Синтаксис зависит от того, работаете ли вы со значением или с указателем.
typedef struct {
int id;
int score;
} Parent;
typedef struct {
Parent parent;
char grade;
} Child;Используя указатель на Child, используйте оператор «стрелка» для доступа к родителю, а затем точку для доступа к его членам:
void update_score(Child* c, int new_score) {
c->parent.score = new_score; // Стрелка, затем точка
}
void print_info(Child* c) {
printf("ID: %d, Score: %d\n", c->parent.id, c->parent.score);
}При использовании значения (передаваемого по копии), используйте точки везде:
void show_id(Child c) {
printf("ID: %d\n", c.parent.id); // Точка, затем точка
}Этот паттерн цепочечного доступа — child->parent.member или child.parent.member — это то, как вы взаимодействуете с унаследованными данными в модели композиции языка C. Это немного более многословно, чем истинное наследование, но это явно и понятно описывает отношения между типами.
Задание
ЛегкоДавайте создадим систему оценок студентов, которая демонстрирует, как получать доступ и изменять данные внутри вложенной структуры. Вы создадите иерархию, в которой Student содержит вложенную структуру Person, а затем напишете функции, которые работают с данными родительской структуры через дочернюю.
Вы организуете свой код в трех файлах:
student.h: Определите иерархию структур с использованием include guards. Создайте структуруPersonс полямиname(символьный массив из 50 символов) иage(целое число). Затем определите структуруStudent, которая включаетPersonв качестве своего первого члена и добавляет полеgrade(целое число, представляющее их балл). Объявите две функции:set_person_age, которая принимаетStudent*и новое значение возраста, иprint_student, которая принимаетStudent*и отображает всю информацию.student.c: Реализуйте обе функции. Ваша функцияset_person_ageдолжна изменять возраст внутри вложенной структурыPerson— здесь вы попрактикуетесь в синтаксисе «стрелка-затем-точка» (s->person.age). Ваша функцияprint_studentдолжна обращаться и отображать все поля как из вложенной структуры person, так и из самой структуры student.main.c: Создайте переменную типаStudentи инициализируйте все ее поля. Затем используйтеset_person_age, чтобы обновить возраст вложенного человека, и вызовитеprint_student, чтобы отобразить результат.
Вы получите четыре входных значения: имя студента (строка), его начальный возраст (целое число), его оценку (целое число) и новый возраст (целое число для обновления через вашу функцию).
В вашем основном файле создайте студента с начальными значениями, затем вызовите set_person_age, чтобы изменить возраст на новое значение, и, наконец, вызовите print_student, чтобы отобразить обновленную информацию.
Ваш вывод должен выглядеть следующим образом:
Name: Emma
Age: 21
Grade: 85Где Emma — это имя, 21 — обновленный возраст (не начальный), а 85 — оценка. Ключевым моментом обучения здесь является навигация через вложенную структуру для чтения и записи членов родительской структуры.
Шпаргалка
Для доступа к членам встроенной родительской структуры из функции используйте синтаксис цепочечного доступа, который зависит от того, работаете ли вы с указателем или со значением.
При использовании указателя на дочернюю структуру используйте оператор «стрелка» для обращения к встроенному родителю, а затем точечную нотацию для доступа к его членам:
void update_score(Child* c, int new_score) {
c->parent.score = new_score; // Стрелка, затем точка
}
void print_info(Child* c) {
printf("ID: %d, Score: %d\n", c->parent.id, c->parent.score);
}При использовании значения (передаваемого по копии) используйте точечную нотацию на всем пути:
void show_id(Child c) {
printf("ID: %d\n", c.parent.id); // Точка, затем точка
}Этот паттерн — child->parent.member или child.parent.member — обеспечивает явный доступ к унаследованным данным в модели композиции C.
Попробуйте сами
#include <stdio.h>
#include <string.h>
#include "student.h"
int main() {
char name[50];
int initial_age, grade, new_age;
// Считать входные данные
fgets(name, 50, stdin);
name[strcspn(name, "\n")] = '\0'; // Удалить символ новой строки
scanf("%d", &initial_age);
scanf("%d", &grade);
scanf("%d", &new_age);
// TODO: Создать переменную типа Student
// TODO: Инициализировать все поля студента
// - Скопировать name в имя вложенного person, используя strcpy
// - Установить возраст вложенного person в значение initial_age
// - Установить оценку grade студента
// TODO: Вызвать set_person_age, чтобы обновить возраст на new_age
// TODO: Вызвать print_student для вывода результата
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга