親メンバーへのアクセス
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 30/61。
構造体の埋め込み(struct embedding)と最初のメンバのルール(first member rule)を理解したところで、実践的な側面に焦点を当てましょう。それは、関数内から埋め込まれた親のデータを読み取り、変更する方法についてです。
関数が Child 構造体(またはそのポインタ)を受け取るとき、親のメンバにアクセスするには、埋め込まれた構造体を経由する必要があります。構文は、値を扱っているかポインタを扱っているかによって異なります。
typedef struct {
int id;
int score;
} Parent;
typedef struct {
Parent parent;
char grade;
} Child;Childへのポインタを使用して、アロー演算子で親(parent)に到達し、次にドット演算子でそのメンバにアクセスします。
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 を含む階層を作成し、子を介して親のデータを操作する関数を作成します。
コードは3つのファイルに分けて構成します:
student.h: インクルードガードを使用して構造体の階層を定義します。name(50文字の文字配列)とage(整数)を持つPerson構造体を作成します。次に、最初のメンバとしてPersonを埋め込み、gradeフィールド(スコアを表す整数)を追加したStudent構造体を定義します。2つの関数を宣言します:Student*と新しい年齢の値を受け取るset_person_age、およびStudent*を受け取りすべての情報を表示するprint_studentです。student.c: 両方の関数を実装します。set_person_age関数は、埋め込まれたPerson構造体内の年齢を変更する必要があります。ここでは、アロー演算子の後にドット演算子を使用する構文(s->person.age)を練習します。print_student関数は、埋め込まれたpersonとstudent自体の両方のすべてのフィールドにアクセスして表示する必要があります。main.c:Student変数を作成し、そのすべてのフィールドを初期化します。次に、set_person_ageを使用して埋め込まれたpersonの年齢を更新し、print_studentを呼び出して結果を表示します。
4つの入力を受け取ります:学生の name(文字列)、initial age(整数)、grade(整数)、および new age(関数を介して更新する整数)。
メインファイルで、初期値を使用して学生を作成し、次に 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: 学生のすべてのフィールドを初期化する
// - strcpy を使用して、埋め込まれた person の name に名前をコピーする
// - 埋め込まれた person の age を initial_age に設定する
// - 学生の grade を設定する
// TODO: set_person_age を呼び出して、年齢を new_age に更新する
// TODO: print_student を呼び出して、結果を表示する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。