قاعدة العضو الأول
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 29 من 61.
عندما تقوم بتضمين struct كـ عضو أول في struct آخر، يحدث شيء قوي: عنوان الذاكرة للـ struct الخارجي مطابق لعنوان الذاكرة لعضوه الأول. هذا مضمون بواسطة معيار C.
تأمل مثالنا السابق:
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // العضو الأول
int grade;
} Child;عندما تقوم بإنشاء متغير Child، يقع العضو Parent في بداية ذاكرة الـ Child تماماً. هذا يعني أن المؤشر إلى Child يشير إلى نفس العنوان تماماً مثل المؤشر إلى عضو الـ Parent الخاص به.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // تحويل آمن!كلا المؤشرين يحملان نفس العنوان. هذا يسمح لك بتمرير Child* إلى أي دالة تتوقع Parent*:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
// في main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // يعمل بشكل مثاليتعتبر هذه التقنية أساس الوراثة في لغة C. يمكن للدالة المكتوبة للنوع الأساسي (base type) أن تعمل على أي نوع مشتق (derived type)، طالما تم تضمين النوع الأساسي في البداية. تعد عملية التحويل (cast) آمنة لأن تخطيطات الذاكرة (memory layouts) تتطابق تماماً.
التحدي
سهلدعنا نستكشف قاعدة العضو الأول من خلال بناء تسلسل هرمي بسيط للحيوانات. ستقوم بإنشاء هيكل أساسي Animal وهيكل مشتق Dog، ثم توضح كيف يمكن تحويل مؤشر إلى Dog بأمان إلى مؤشر إلى Animal.
البصيرة الرئيسية التي ستطبقها هي: عندما يكون Animal هو العضو الأول في Dog، يشترك كلا الهيكلين في نفس عنوان ذاكرة البداية. هذا يعني أنه يمكنك تمرير Dog* إلى أي وظيفة تتوقع Animal* عن طريق تحويل المؤشر (casting).
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
animal.h: قم بتعريف كلا الهيكلين مع استخدام include guards. يجب أن يحتوي هيكلAnimalالخاص بك على حقل واحد:legs(عدد صحيح يمثل عدد الأرجل). يجب أن يدمج هيكلDogالخاص بكAnimalكـ عضو أول ويضيف حقلname(مصفوفة أحرف مكونة من 50 حرفاً). قم أيضاً بالتصريح عن وظيفةprint_animalالتي تأخذ معاملAnimal*.animal.c: قم بتنفيذ وظيفةprint_animal. تعمل هذه الوظيفة مع النوع الأساسي — فهي تعرف فقط عنAnimalوتطبع عدد الأرجل التي يمتلكها الحيوان.main.c: قم بإنشاء متغيرDogواملأ حقوله (كل من أرجل الحيوان المدمج واسم الكلب). ثم وضح قاعدة العضو الأول عن طريق تحويلDog*الخاص بك إلىAnimal*وتمريره إلىprint_animal. قم أيضاً بطباعة اسم الكلب بشكل منفصل لإظهار الصورة الكاملة.
ستتلقى مدخلين: اسم الكلب (سلسلة نصية) و عدد الأرجل (عدد صحيح).
يجب أن يبدو مخرجك كما يلي:
Dog: Buddy
Legs: 4حيث Buddy هو اسم الكلب و 4 هو عدد الأرجل. اطبع اسم الكلب أولاً من main.c، ثم استدعِ print_animal مع المؤشر المحول لعرض الأرجل.
يوضح هذا كيف يمكن لوظيفة مكتوبة للنوع الأساسي (Animal) أن تعمل بسلاسة مع نوع مشتق (Dog) من خلال تحويل المؤشر — وهو أساس الوراثة في لغة C.
ورقة مرجعية
عندما يتم تضمين هيكل (struct) كـ عضو أول في هيكل آخر، يشترك كلا الهيكلين في نفس عنوان الذاكرة البدائي. هذا مضمون بواسطة معيار C.
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // العضو الأول
int grade;
} Child;يمكن تحويل مؤشر (pointer) للهيكل الخارجي بأمان إلى مؤشر لعضوه الأول:
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // تحويل آمن - نفس العنوانيسمح هذا بتمرير مؤشر من نوع مشتق إلى دوال تتوقع النوع الأساسي:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // يعمل لأن تخطيطات الذاكرة متوافقةهذه التقنية هي أساس الوراثة في لغة C، مما يُمكّن الدوال المكتوبة للأنواع الأساسية من العمل على الأنواع المشتقة.
جرّب بنفسك
#include <stdio.h>
#include <string.h>
#include "animal.h"
int main() {
// قراءة المدخلات
char name[50];
int legs;
scanf("%s", name);
scanf("%d", &legs);
// TODO: إنشاء متغير من نوع Dog
// TODO: تعيين اسم الكلب باستخدام strcpy
// TODO: تعيين عدد الأرجل (الوصول من خلال Animal المضمن)
// TODO: طباعة اسم الكلب بالتنسيق: "Dog: <name>"
// TODO: تحويل Dog* إلى Animal* واستدعاء print_animal
// هذا يوضح قاعدة العضو الأول!
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة