مغلف عام
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 50 من 61.
يمكن لـ void* بمفرده تخزين أي بيانات، لكنه يعاني من عيب جوهري: تفقد القدرة على معرفة نوع البيانات التي يخزنها. ولإنشاء حاويات عامة مفيدة حقًا، نحتاج إلى إقران مؤشر البيانات بمعلومات النوع. وهنا يأتي دور نمط الغلاف العام.
الفكرة بسيطة: أنشئ بنية تجمع بين void* وتعداد يحدد النوع المخزّن. بهذه الطريقة، يعرف الغلاف دائمًا كيفية تفسير محتوياته.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;عند تخزين البيانات، تخصّص ذاكرة للقيمة الفعلية وتسجّل نوعها. عند الاسترداد، تتحقق من النوع قبل إجراء التحويل:
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}
void print_wrapper(Wrapper* w) {
if (w->type == TYPE_INT) {
printf("%d\n", *(int*)w->data);
} else if (w->type == TYPE_STRING) {
printf("%s\n", (char*)w->data);
}
}يُعَدّ هذا النمط أساسًا لبناء حاويات عامة آمنة من حيث النوع في لغة C. تعمل التعدادات كوسم نوع وقت التشغيل، مما يتيح لشفرتك اتخاذ قرارات آمنة بشأن كيفية التعامل مع البيانات المخزّنة.
التحدي
سهللننشئ نظام تغليف عامًا وآمنًا من حيث النوع يمكنه تخزين عدد صحيح أو سلسلة نصية، مع تتبّع النوع الذي يحتويه حتى نتمكن من استرجاع البيانات وطباعتها بأمان لاحقًا.
ستنظّم الشيفرة عبر ثلاثة ملفات:
wrapper.h: عرّف تعدادًا من نوعDataTypeبالقيمتينTYPE_INTوTYPE_STRING. ثم عرّف بنيةWrapperالتي تجمع مؤشر بيانات من نوعvoid*مع حقل من نوعDataType. صرّح بالنماذج الأولية للدوال اللازمة لإنشاء الأغلفة وطباعتها.wrapper.c: نفّذ وظائف التغليف:wrap_int: تستقبل عددًا صحيحًا، وتخصص ذاكرة له، وتخزّن القيمة، ثم تعيدWrapperمع وسم النوع المناسبwrap_string: تستقبل سلسلة نصية (char*)، وتخصص ذاكرة وتنسخ السلسلة، ثم تعيدWrapperمع وسم نوع السلسلةprint_wrapper: تتحقق من وسم النوع وتطبع البيانات بالطريقة المناسبة (العدد الصحيح كما هو، والسلسلة كما هي)free_wrapper: تحرر البيانات المخصصة ديناميكيًا داخل الغلاف
main.c: اجمع كل ذلك معًا. اقرأ مؤشر نوع (iللعدد الصحيح أوsللسلسلة) متبوعًا بالقيمة. أنشئ الغلاف المناسب، واطبع محتوياته، ثم حرر الذاكرة.
سيتلقى برنامجك مدخلين:
- مؤشر نوع:
iللعدد الصحيح أوsللسلسلة - القيمة المطلوب تغليفها
مثال على الخرج عندما تكون المدخلات i و42:
42مثال على الخرج عندما تكون المدخلات s وHello:
Helloمثال على الخرج عندما تكون المدخلات i و-100:
-100مثال على الخرج عندما تكون المدخلات s وGeneric Programming:
Generic Programmingتذكّر استخدام حواجز التضمين في ملف الترويسة. ولنسخ السلاسل، خصص مساحة كافية للسلسلة بالإضافة إلى المحرف الصفري النهائي باستخدام strlen وstrcpy.
جرّب بنفسك
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // استهلك السطر الجديد
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: أنشئ غلافًا لعدد صحيح باستخدام wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// أزل السطر الجديد اللاحق إن وُجد
str[strcspn(str, "\n")] = '\0';
// TODO: أنشئ غلافًا لسلسلة نصية باستخدام wrap_string
}
// TODO: اطبع محتويات الغلاف باستخدام print_wrapper
// TODO: حرر ذاكرة الغلاف باستخدام free_wrapper
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 عبر الإنترنت