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

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

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

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

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".

تتبع الأصناف المتداخلة (Nested classes) نفس قواعد الوصول التي تعلمتها. فالصنف المتداخل المصرح عنه في القسم الخاص (private) لا يمكن الوصول إليه إلا من داخل الصنف الخارجي.

عندما يتم التصريح عنه كـ public، يمكن للكود الخارجي استخدامه مع معامل النطاق (scope operator):

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

تفصيل مهم واحد حول الاتجاه: الفئة المتداخلة (nested class) هي عضو في الفئة المحيطة ولديها نفس حقوق الوصول مثل أي عضو آخر، لذا يمكنها الوصول إلى الأعضاء الخاصين (private members) للفئة المحيطة دون الحاجة إلى إعلان صديق (friend declaration). ومع ذلك، فإن العكس ليس تلقائياً — فالفئة المحيطة لا تملك صلاحية الوصول إلى الأعضاء الخاصين للفئة المتداخلة.

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

challenge icon

التحدي

سهل

لنقم ببناء نظام بسيط لإدارة المهام حيث يتم تنظيم المهام داخل مشروع. هذا سيناريو مثالي للفئات المتداخلة (nested classes) — فالمهمة Task لا يكون لها معنى إلا في سياق المشروع Project، لذا سنقوم بتداخلها بالداخل للحفاظ على تنظيم الكود وتجنب تلويث النطاق العام (global namespace).

ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:

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

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

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

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

  • 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: إنشاء مشروع بالاسم المعطى
    
    // TODO: إضافة كلتا المهمتين إلى المشروع
    
    // TODO: عرض الحالة الأولية
    
    // TODO: إكمال المهمة الأولى
    
    // TODO: عرض الحالة مرة أخرى لإظهار التغيير
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming