مؤشرات الدوال في الـ Structs
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 38 من 61.
حتى الآن، استخدمنا مؤشرات الدوال كمتغيرات مستقلة أو مررناها كوسائط. والخطوة التالية نحو تعدد الأشكال هي تضمين مؤشر دالة مباشرةً داخل بنية، مما يمنح البنية سلوكها الخاص.
عندما تحتوي بنية على مؤشر function، يمكن لكل instance الاحتفاظ بـ function مختلفة. وهذا يعني أن كائنين من النوع نفسه يمكن أن يتصرفا بشكل مختلف عند استدعاء function pointer الخاص بهما:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // مؤشر دالة كعضو
} Printer;تحتوي بنية Printer الآن على عضوين: البيانات (name) والسلوك (print). لاستخدامها، عليك تنفيذ assign دالة إلى المؤشر أثناء التهيئة:
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;
}لاحظ كيف نستدعي الدالة من خلال البنية: p.print("Hello"). يبدو هذا مشابهًا بشكل لافت لاستدعاء أسلوب على كائن في لغات أخرى. تحمل البنية بياناتها والدالة التي تعمل عليها معًا.
هذا النمط هو الأساس لتعدد الأشكال في C: يمكن أن تكون لدى الحالات المختلفة دوال مختلفة مُعيَّنة، مما ينتج سلوكيات مختلفة من نوع البنية نفسه.
التحدي
سهللنبنِ نظام Notifier يوضّح كيف يمكن للـ structs حمل سلوكها الخاص من خلال مؤشرات الدوال.
ستنشئ ملفين لتنظيم التعليمات البرمجية:
notifier.h: عرّف نوع مؤشر دالة يُسمّىNotifyFunc، ويأخذ معاملconst char*ولا يُرجع شيئًا. ثم عرّف struct من نوعNotifierيحتوي علىname(من النوعconst char*) ومؤشر دالةnotifyمن النوعNotifyFunc.main.c: أدرج ملف الترويسة الخاص بك ونفّذ دالتي notification:alert_notify: تطبع message بالتنسيق:[ALERT] messageinfo_notify: تطبع message بالتنسيق:[INFO] message
Notifier، واربط function المناسبة استنادًا إلى input، واستدعِ notification من خلال مؤشر function الخاص بالـ struct.
سيستقبل برنامجك inputين: نوع notification (1 للتنبيه، و2 للمعلومات) وmessage لعرضها.
استنادًا إلى النوع، عيّن function المطابقة إلى member notify الخاص بـ notifier، ثم استدعها من خلال الـ struct لعرض message.
مثال على المخرجات عندما يكون النوع 1 وmessage هو "System starting":
[ALERT] System startingمثال على المخرجات عندما يكون النوع 2 وmessage هو "All systems normal":
[INFO] All systems normalتذكّر استخدام include guards في ملف الترويسة، واستدعاء function من خلال member الخاص بالـ struct: n.notify(message)
جرّب بنفسك
#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: أنشئ كائن Notifier
// TODO: بناءً على type (1 للتنبيه، 2 للمعلومات)،
// عيّن الدالة المناسبة لعضو notify في الـ notifier
// TODO: استدعِ الإشعار عبر مؤشر الدالة في الـ struct
// استخدم: n.notify(message)
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكالتدرّب بنفسك: مترجم C عبر الإنترنت