قاعدة العضو الأول
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 29 من 61.
عندما تقوم بتضمين بنية باعتبارها first member في بنية أخرى، يحدث أمر قوي: يكون عنوان ذاكرة البنية الخارجية مطابقًا لعنوان ذاكرة عضوها الأول. ويضمن معيار C ذلك.
تأمل مثالنا السابق:
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // العضو الأول
int grade;
} Child;عند إنشاء متغير Child، يوجد عضو Parent في بداية Child من memory تمامًا. وهذا يعني أن المؤشر إلى Child يشير إلى نفس address تمامًا الذي يشير إليه المؤشر إلى عضو Parent الخاص به.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // تحويل آمن!يحمل كلا المؤشرين نفس address. يتيح لك ذلك تمرير Child* إلى أي function تتوقع Parent*:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
// في main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // يعمل بشكل مثاليهذه التقنية هي أساس الوراثة في C. يمكن لـ function مكتوبة للنوع الأساسي أن تعمل على أي نوع مشتق، ما دام النوع الأساسي embedded أولًا. إن cast آمن لأن memory layouts align perfectly.
التحدي
سهللنستكشف قاعدة first member من خلال بناء تسلسل هرمي بسيط للحيوانات. ستنشئ بنية Animal أساسية وبنية Dog مشتقة، ثم توضّح كيف يمكن تحويل مؤشر إلى Dog بأمان إلى مؤشر إلى Animal.
الفكرة الأساسية التي ستطبّقها: عندما تكون Animal هي first member في Dog، تشترك البنيتان في عنوان الذاكرة الابتدائي نفسه. وهذا يعني أنه يمكنك تمرير Dog* إلى أي function تتوقع Animal* من خلال تحويل المؤشر.
ستنظّم الشيفرة عبر ثلاثة ملفات:
animal.h: عرّف البنيتين مع حواجز include. يجب أن تحتوي بنيةAnimalعلى field واحدة:legs(عدد صحيح يمثّل عدد الأرجل). ويجب أن تضع بنيةDogبنيةAnimalالمضمّنة باعتبارها first member وتضيف field باسمname(مصفوفة محارف مكوّنة من 50 محرفًا). أعلن أيضًا عن function باسمprint_animalتأخذAnimal*parameter.animal.c: نفّذ functionprint_animal. تعمل هذه function مع النوع الأساسي. وهي تعرف فقطAnimalوتطبع عدد الأرجل التي يملكها الحيوان.main.c: أنشئ متغيرًا من النوعDogواملأ حقوله (كلًّا من legs الخاصة بالحيوان المضمّن واسم الكلب). ثم وضّح قاعدة first member من خلال تحويلDog*الخاص بك إلىAnimal*وتمريره إلىprint_animal. اطبع أيضًا اسم الكلب بشكل منفصل لإظهار الصورة الكاملة.
ستتلقى مدخلين: اسم الكلب (سلسلة نصية) وعدد الأرجل (عدد صحيح).
يجب أن يبدو الإخراج بهذا الشكل:
Dog: Buddy
Legs: 4حيث إن Buddy هو اسم الكلب و4 هو عدد الأرجل. اطبع اسم الكلب أولًا من main.c، ثم استدعِ print_animal باستخدام المؤشر المحوَّل لعرض عدد الأرجل.
يوضّح هذا كيف يمكن لـ function مكتوبة للنوع الأساسي (Animal) أن تعمل بسلاسة مع نوع مشتق (Dog) من خلال تحويل المؤشرات: وهو أساس الوراثة في 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;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكالتدرّب بنفسك: مترجم C عبر الإنترنت