الوصول إلى أعضاء الأب
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 30 من 61.
الآن بعد أن فهمنا تضمين الهياكل (struct embedding) وقاعدة العضو الأول (first member rule)، دعونا نركز على الجانب العملي: كيفية قراءة وتعديل بيانات الأب المضمنة من داخل دالة.
عندما تستقبل دالة هيكل Child (أو مؤشرًا إليه)، فإن الوصول إلى أعضاء الأب يتطلب التنقل عبر الهيكل المضمن. تعتمد الصيغة البرمجية على ما إذا كنت تتعامل مع قيمة أو مؤشر.
typedef struct {
int id;
int score;
} Parent;
typedef struct {
Parent parent;
char grade;
} Child;باستخدام مؤشر (pointer) إلى Child، استخدم عامل السهم (arrow operator) للوصول إلى 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 — هو الطريقة التي تتفاعل بها مع البيانات الموروثة في نموذج التركيب (composition model) الخاص بلغة C. إنه أكثر إسهاباً قليلاً من الوراثة الحقيقية، لكنه صريح وواضح بشأن العلاقة بين الأنواع.
التحدي
سهللنقم ببناء نظام لدرجات الطلاب يوضح كيفية الوصول إلى البيانات وتعديلها داخل هيكل (struct) متداخل. ستقوم بإنشاء تسلسل هرمي حيث يحتوي Student على Person متداخل، ثم تكتب دوالاً تتعامل مع بيانات الأب من خلال الابن.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
student.h: قم بتعريف التسلسل الهرمي للهياكل (structs) مع استخدام حراس التضمين (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إلى جميع الحقول وتعرضها من كل من الشخص المتداخل والطالب نفسه.main.c: قم بإنشاء متغير من نوعStudentوقم بتهيئة جميع حقوله. ثم استخدمset_person_ageلتحديث عمر الشخص المتداخل، واستدعِprint_studentلعرض النتيجة.
ستتلقى أربعة مدخلات: اسم الطالب (سلسلة نصية)، وعمره الأولي (عدد صحيح)، ودرجته (عدد صحيح)، وعمره الجديد (عدد صحيح لتحديثه عبر دالتك).
في ملفك الرئيسي، قم بإنشاء الطالب بالقيم الأولية، ثم استدعِ set_person_age لتغيير العمر إلى القيمة الجديدة، وأخيراً استدعِ print_student لعرض المعلومات المحدثة.
يجب أن يبدو مخرجك كالتالي:
Name: Emma
Age: 21
Grade: 85حيث Emma هو الاسم، و 21 هو العمر المحدث (وليس العمر الأولي)، و 85 هي الدرجة. التعلم الأساسي هنا هو التنقل عبر الهيكل المتداخل لقراءة وكتابة أعضاء الأب.
ورقة مرجعية
للوصول إلى أعضاء الـ struct الأب المضمنة من دالة، استخدم صيغة الوصول المتسلسل التي تعتمد على ما إذا كنت تتعامل مع مؤشر (pointer) أو قيمة (value).
مع وجود مؤشر إلى الـ struct الابن، استخدم عامل السهم (arrow operator) للوصول إلى الأب المضمن، ثم تدوين النقطة (dot notation) للوصول إلى أعضائه:
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 — يوفر وصولاً صريحاً إلى البيانات الموروثة في نموذج التركيب (composition model) الخاص بلغة 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
// - تعيين عمر الشخص المضمن إلى initial_age
// - تعيين درجة الطالب
// TODO: استدعاء set_person_age لتحديث العمر إلى new_age
// TODO: استدعاء print_student لعرض النتيجة
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة