مؤشرات الدوال في الـ Structs
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 38 من 61.
حتى الآن، استخدمنا مؤشرات الدوال كمتغيرات مستقلة أو مررناها كوسائط. الخطوة التالية نحو تعدد الأشكال (polymorphism) هي تضمين مؤشر دالة مباشرة داخل هيكل (struct) — مما يمنح الهيكل سلوكاً خاصاً به.
عندما يحتوي الـ struct على مؤشر دالة، يمكن لكل مثيل أن يحمل دالة مختلفة. وهذا يعني أن كائنين من نفس النوع يمكن أن يتصرفا بشكل مختلف عند استدعاء مؤشر الدالة الخاص بهما:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // مؤشر دالة كعضو
} Printer;يحتوي هيكل Printer الآن على عضوين: البيانات (name) والسلوك (print). لاستخدامه، تقوم بتعيين دالة للمؤشر أثناء التهيئة:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // الناتج: !!! Hello !!!
return 0;
}لاحظ كيف نستدعي الدالة من خلال الـ struct: p.print("Hello"). يبدو هذا مشابهاً بشكل ملحوظ لاستدعاء method على كائن في لغات أخرى. يحمل الـ struct كلاً من بياناته والدالة التي تعمل عليها.
هذا النمط هو أساس تعدد الأشكال (polymorphism) في لغة C — حيث يمكن تخصيص دوال (functions) مختلفة لمثيلات (instances) مختلفة، مما ينتج سلوكيات مختلفة من نفس نوع الـ struct.
التحدي
سهللنقم ببناء نظام Notifier يوضح كيف يمكن للهياكل (structs) أن تحمل سلوكها الخاص من خلال مؤشرات الدوال (function pointers).
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
notifier.h: قم بتعريف نوع مؤشر دالة يسمىNotifyFuncيأخذ معاملًا من نوعconst char*ولا يعيد شيئاً. ثم قم بتعريف هيكلNotifierيحتوي علىname(من نوعconst char*) ومؤشر دالةnotifyمن النوعNotifyFunc.main.c: قم بتضمين ملف الرأس الخاص بك وتنفيذ دالتي إشعار:alert_notify— تطبع الرسالة بالتنسيق التالي:[ALERT] messageinfo_notify— تطبع الرسالة بالتنسيق التالي:[INFO] message
Notifier، وقم بربط الدالة المناسبة بناءً على المدخلات، واستدعِ الإشعار من خلال مؤشر الدالة الخاص بالهيكل.
سيتلقى برنامجك مدخلين: نوع الإشعار (1 للتنبيه "alert"، و 2 للمعلومات "info") و رسالة لعرضها.
بناءً على النوع، قم بتعيين الدالة المقابلة لعضو notify في الـ notifier الخاص بك، ثم استدعها من خلال الهيكل لعرض الرسالة.
مثال على المخرجات عندما يكون النوع 1 والرسالة "System starting":
[ALERT] System startingمثال على المخرجات عندما يكون النوع 2 والرسالة "All systems normal":
[INFO] All systems normalتذكر استخدام حراس التضمين (include guards) في ملف الرأس الخاص بك، واستدعاء الدالة من خلال عضو الهيكل: n.notify(message)
ورقة مرجعية
يمكن تضمين مؤشرات الدوال مباشرة داخل الهياكل (structs)، مما يمنح كل مثيل (instance) سلوكه الخاص. هذا هو الأساس لتعدد الأشكال (polymorphism) في لغة C.
تعريف هيكل (Struct) بمؤشر دالة
أولاً، قم بتعريف نوع مؤشر دالة، ثم قم بتضمينه كعضو في الهيكل:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Function pointer as a member
} Printer;استخدام الهيكل
قم بتعيين دالة للمؤشر أثناء التهيئة، ثم استدعها من خلال الهيكل:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Output: !!! Hello !!!
return 0;
}الصيغة p.print("Hello") تستدعي الدالة من خلال الهيكل، بشكل مشابه لاستدعاء تابع (method) على كائن (object) في لغات أخرى.
سلوك تعدد الأشكال (Polymorphic Behavior)
يمكن أن يكون لمثيلات مختلفة من نفس نوع الهيكل دوال مختلفة معينة لها، مما ينتج سلوكيات مختلفة من نفس نوع الهيكل.
جرّب بنفسك
#include <stdio.h>
#include "notifier.h"
// TODO: تنفيذ دالة alert_notify
// يجب أن تطبع: [ALERT] message
// TODO: تنفيذ دالة info_notify
// يجب أن تطبع: [INFO] message
int main() {
int type;
char message[256];
// قراءة المدخلات
scanf("%d", &type);
getchar(); // استهلاك السطر الجديد
fgets(message, sizeof(message), stdin);
// إزالة السطر الجديد الزائد من message إذا وجد
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: إنشاء مثيل (instance) من Notifier
// TODO: بناءً على type (1 للتنبيه، 2 للمعلومات)،
// تعيين الدالة المناسبة للعضو notify الخاص بـ notifier
// TODO: استدعاء الإشعار من خلال مؤشر الدالة (function pointer) الخاص بـ struct
// استخدم: n.notify(message)
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب8تعدد الأشكال (Polymorphism)
مؤشرات الدوال في الـ Structsمحاكاة الـ Methodsمفهوم الـ Interfaceتنفيذ الـ Interfacesالتكرار متعدد الأشكالمراجعة: Greeter3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة