Menu
CoddyTech
flag Ar iconالعربيةdown icon

مغلف عام

جزء من قسم البرمجة كائنية التوجه في رحلة 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. تعمل التعدادات كوسم نوع وقت التشغيل، مما يتيح لشفرتك اتخاذ قرارات آمنة بشأن كيفية التعامل مع البيانات المخزّنة.

challenge icon

التحدي

سهل

لننشئ نظام تغليف عامًا وآمنًا من حيث النوع يمكنه تخزين عدد صحيح أو سلسلة نصية، مع تتبّع النوع الذي يحتويه حتى نتمكن من استرجاع البيانات وطباعتها بأمان لاحقًا.

ستنظّم الشيفرة عبر ثلاثة ملفات:

  • 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 للسلسلة) متبوعًا بالقيمة. أنشئ الغلاف المناسب، واطبع محتوياته، ثم حرر الذاكرة.

سيتلقى برنامجك مدخلين:

  1. مؤشر نوع: i للعدد الصحيح أو s للسلسلة
  2. القيمة المطلوب تغليفها

مثال على الخرج عندما تكون المدخلات 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;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C عبر الإنترنت