Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

تعريف الـ Opaque Structs

جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 18 من 61.

الآن بعد أن فهمت مفهوم المؤشرات المعتمة (opaque pointers)، دعنا نرى كيفية إكمال هذا النمط. يصرح ملف الترويسة (header) بوجود نوع ما، ولكن ملف المصدر (source file) هو المكان الذي تحدد فيه ما يوجد بداخله فعلياً.

إليك الإعداد الكامل. أولاً، ملف الترويسة (header) الذي يحتوي فقط على التصريح المسبق (forward declaration):

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // نوع غير مكتمل (Incomplete type)

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

بعد ذلك، يحتوي ملف المصدر على التعريف الفعلي للـ struct:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // مخفي عن الخارج!
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

النقطة الأساسية: struct Counter { ... } يظهر فقط في ملف .c. أي ملف يتضمن counter.h يعرف أن Counter موجود ولكنه لا يستطيع رؤية value أو step. محاولة كتابة c->value في main.c ستؤدي إلى خطأ في المترجم (compiler error) - فالأعضاء غير مرئيين حقاً.

هذا الفصل هو أساس التغليف (encapsulation) في لغة C. الخطوة التالية هي توفير وصول متحكم فيه من خلال دوال الـ getter والـ setter.

challenge icon

التحدي

سهل

لنقم ببناء وحدة Timer توضح نمط المؤشر المعتم (opaque pointer pattern). ستتدرب على إخفاء تعريف الهيكل (struct) داخل ملف المصدر، مما يجعل أعضاءه الداخلية غير مرئية تماماً لأي كود يستخدم وحدتك.

ستقوم بإنشاء ثلاثة ملفات:

  • timer.h: هذه هي واجهتك العامة. قم بالتصريح عن نوع Timer معتم باستخدام تصريح مسبق (forward declaration) فقط — لا تضع جسم الهيكل هنا! قم أيضاً بالتصريح عن دالتين: create_timer (تأخذ عدداً صحيحاً للثواني وتعيد مؤشر Timer) و free_timer (تأخذ مؤشر Timer وتقوم بالتنظيف). استخدم حراس التضمين (include guards) مع الرمز TIMER_H.
  • timer.c: هذا هو المكان الذي يعيش فيه السر. قم بتعريف struct Timer الفعلي مع عضوين مخفيين: int seconds و int running (استخدم 1 للتشغيل، و 0 للإيقاف). قم بتنفيذ المنشئ (constructor) لتخصيص الهيكل، وتهيئة seconds بالقيمة المقدمة، وتعيين running إلى 1. قم بتنفيذ المدمر (destructor) لتحرير الذاكرة.
  • main.c: استخدم وحدة Timer الخاصة بك لإنشاء مؤقت والتأكد من إنشائه بنجاح. نظراً لأن أعضاء الهيكل مخفية، فلا يمكنك الوصول إليها مباشرة — وهذا هو الهدف الأساسي! ببساطة اطبع رسالة تؤكد الإنشاء، ثم قم بتحرير المؤقت واطبع رسالة تنظيف.

ستتلقى مدخلاً واحداً: عدد الثواني (عدد صحيح) لتهيئة المؤقت به.

في ملفك الرئيسي، قم بإنشاء مؤقت باستخدام قيمة الثواني المقدمة. اطبع رسالة إنشاء، ثم قم بتحرير المؤقت واطبع تأكيداً.

اطبع المخرجات بهذا التنسيق:

Timer created
Timer freed

البصيرة الأساسية هنا هي أن main.c يمكنه العمل مع مؤشرات Timer ولكنه لا يملك طريقة للوصول إلى seconds أو running مباشرة. محاولة كتابة t->seconds في main.c ستؤدي إلى خطأ في المترجم لأن جسم الهيكل محدد فقط في timer.c. هذا هو التغليف (encapsulation) الحقيقي — تفاصيل التنفيذ مخفية تماماً عن مستخدمي وحدتك.

ورقة مرجعية

يفصل نمط المؤشر المعتم (opaque pointer pattern) بين التصريح عن النوع وتعريفه، مما يخفي تفاصيل التنفيذ.

يحتوي ملف الترويسة (Header file) على تصريح مسبق فقط:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // نوع غير مكتمل

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

يحتوي ملف المصدر (Source file) على التعريف الفعلي للهيكل (struct):

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // مخفي عن الخارج
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

يظهر تعريف الهيكل (struct) فقط في ملف .c. الملفات التي تتضمن الترويسة تعرف بوجود النوع ولكن لا يمكنها الوصول إلى أعضائه مباشرة. يوفر هذا تغليفاً (encapsulation) حقيقياً في لغة C.

جرّب بنفسك

#include <stdio.h>
#include "timer.h"

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: إنشاء مؤقت بالقيمة المعطاة لـ seconds
    
    // TODO: طباعة "Timer created"
    
    // TODO: تحرير المؤقت
    
    // TODO: طباعة "Timer freed"
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming