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

الأصناف المتداخلة والداخلية

جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 38 من 104.

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

class LinkedList {
public:
    class Node {    // فئة متداخلة
        int data;
        Node* next;
    public:
        Node(int d) : data(d), next(nullptr) {}
        friend class LinkedList;
    };
    
private:
    Node* head;
    
public:
    LinkedList() : head(nullptr) {}
};

الفئة المتداخلة Node مرتبطة ارتباطًا وثيقًا بـ LinkedList — فهي لا تكون منطقية إلا في ذلك السياق. ومن خلال تداخلها، نوضح هذه العلاقة ونتجنب تلويث النطاق العام باسم عام مثل "Node".

تتبع الفئات المتداخلة قواعد الوصول نفسها التي تعلمتها. لا يمكن الوصول إلى الفئة المتداخلة المُعلنة في القسم الخاص إلا داخل الفئة الخارجية.

عند التصريح عنه باستخدام public، يمكن للكود الخارجي استخدامه باستخدام عامل النطاق:

LinkedList::Node node(42);    // فقط إذا كان Node public

تفصيل مهم حول الاتجاه: تُعدّ Nested class عضوًا في class الحاوية، ولها حقوق الوصول نفسها التي يتمتع بها أي عضو آخر، لذا يمكنها الوصول إلى الأعضاء الخاصة في class الحاوية من دون إعلان friend. لكن العكس ليس تلقائيًا: فلا تملك class الحاوية صلاحية الوصول إلى الأعضاء الخاصة في Nested class.

لهذا السبب يصرّح Node بالعبارة friend class LinkedList; في المثال أعلاه: يمنح تصريح friend الصنف LinkedList المحيط إمكانية الوصول إلى الأعضاء الخاصة بـ Node، وهو الاتجاه الوحيد الذي لا يحدث تلقائيًا.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا بسيطًا لإدارة المهام، حيث تُنظَّم المهام ضمن مشروع. هذا سيناريو مثالي للفئات المتداخلة: لا يكون لـ Task معنى إلا ضمن سياق Project، لذلك سنضعها متداخلة بداخله للحفاظ على تنظيم الشفرة وتجنّب تلويث namespace العام.

ستنشئ ملفين لتنظيم شفرتك:

  • Project.h: عرّف فئة Project تحتوي على فئة Task متداخلة.

    يجب التصريح عن فئة Task المتداخلة ضمن القسم public في Project حتى يمكن الوصول إليها من الخارج. يجب أن تخزّن اسم مهمة (string) وحالة اكتمال (bool). امنحها مُنشئًا يأخذ الاسم ويضبط المهمة على أنها incomplete افتراضيًا. أدرج الدوال complete() لوضع علامة done عليها، وisComplete() للتحقق من حالتها، وgetName() لاسترداد الاسم.

    يجب أن تخزّن فئة Project نفسها اسم مشروع (string) ومجموعة من مؤشرات Task. وفّر مُنشئًا يأخذ اسم المشروع، ودالة addTask(const std::string& taskName) تنشئ مهمة جديدة وتضيفها إلى المشروع، ودالة completeTask(const std::string& taskName) تبحث عن مهمة بالاسم وتكملها، ودالة displayStatus() تعرض اسم المشروع متبوعًا بحالة كل مهمة.

    بما أن Task متداخلة داخل Project، يمكن لفئة Project التعامل مع المهام مباشرةً. لا تنسَ تنظيف المهام المخصّصة dynamically في المُدمّر.

  • main.cpp: اجمع كل شيء معًا من خلال قراءة اسم مشروع واسمي مهمتين من الإدخال (ثلاثة أسطر إجمالًا). أنشئ Project بالاسم المُعطى، وأضف المهمتين إليه، ثم اعرض الحالة الأولية. أكمل المهمة الأولى واعرض الحالة مرة أخرى لإظهار التغيير.

    يجب أن يكون تنسيق الإخراج الخاص بـ displayStatus() كما يلي:

    Project: <project_name>
      - <task_name>: [incomplete]
      - <task_name>: [complete]

    استخدم [complete] أو [incomplete] بناءً على حالة كل مهمة.

يوضح هذا التحدي كيف تساعد الفئات المتداخلة في تنظيم الأنواع المرتبطة: فئة Task مرتبطة ارتباطًا وثيقًا بـ Project، ولن يكون لها معنى بمفردها. ومن خلال تداخلها، نوضّح هذه العلاقة بجلاء في بنية شفرتنا.

جرّب بنفسك

#include <iostream>
#include <string>
#include "Project.h"

using namespace std;

int main() {
    // اقرأ المدخلات
    string projectName;
    string task1Name;
    string task2Name;
    
    getline(cin, projectName);
    getline(cin, task1Name);
    getline(cin, task2Name);
    
    // TODO: أنشئ Project بالاسم المعطى
    
    // TODO: أضف كلتا المهمتين إلى المشروع
    
    // TODO: اعرض الحالة الأولية
    
    // TODO: أكمل المهمة الأولى
    
    // TODO: اعرض الحالة مرة أخرى لإظهار التغيير
    
    return 0;
}
quiz iconاختبر نفسك

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

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

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