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

تعمق في الموادم

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

إنّ المُهدم (destructor) هو دالة عضو خاصة تعمل تلقائياً عندما يتم تدمير كائن. مهمته الأساسية هي تحرير الموارد التي اكتسبها الكائن خلال فترة حياته، مثل الذاكرة المخصصة ديناميكياً، أو مقابض الملفات، أو اتصالات الشبكة.

يملك المدمّر (destructor) نفس اسم الفئة (class)، مسبوقاً بعلامة التلدة ~. وهو لا يأخذ أي معاملات وليس له نوع إرجاع:

class FileHandler {
    std::string filename;
    int* buffer;
public:
    FileHandler(std::string name, size_t size) 
        : filename(name), buffer(new int[size]) {
        std::cout << "Opening " << filename << "\n";
    }
    
    ~FileHandler() {
        delete[] buffer;
        std::cout << "Closing " << filename << "\n";
    }
};

تُستدعى المُهدمات (Destructors) تلقائياً في هذه الحالات:

  • تخرج كائنات الـ Stack عن النطاق
  • يتم استدعاء delete على كائن مخصص في الـ heap
  • تنتهي دورة حياة كائن مؤقت
void example() {
    FileHandler f1("data.txt", 100);    // تم استدعاء المنشئ (Constructor)
    
    FileHandler* f2 = new FileHandler("log.txt", 50);
    delete f2;                           // تم استدعاء المدمر (Destructor) لـ f2
    
}   // تم استدعاء المدمر لـ f1 (خرج عن النطاق)

على عكس المنشئات (constructors)، يمكن للفئة (class) أن تحتوي على مدمر واحد فقط (one destructor). لا يمكنك تحميله بشكل زائد (overload).

إذا لم تقم بتعريف واحد، يقوم المترجم (compiler) بإنشاء مدمر افتراضي (default destructor) لا يفعل شيئاً خاصاً - فهو ببساطة يقوم بتدمير كل عضو. بالنسبة للفئات (classes) التي تدير الموارد، يجب عليك كتابة المدمر الخاص بك لمنع تسرب الذاكرة (memory leaks).

challenge icon

التحدي

سهل

لنقم ببناء مدير جلسات يتتبع جلسات المستخدمين النشطة ويوضح كيف تقوم المدمرات (destructors) بتنظيف الموارد تلقائيًا عند تدمير الكائنات — سواء خرجت عن النطاق (out of scope) أو تم حذفها صراحةً.

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

  • Session.h: قم بتعريف فئة Session تمثل جلسة مستخدم نشطة مع بيانات مخصصة ديناميكيًا. يجب أن تحتوي الفئة على:
    • أعضاء خاصين (Private members): username (سلسلة نصية string)، و sessionId (عدد صحيح int)، ومؤشر لمصفوفة أعداد صحيحة يسمى activityLog يتتبع إجراءات المستخدم، بالإضافة إلى logSize لحجم المصفوفة.
    • منشئ (constructor) يأخذ اسم المستخدم، ومعرف الجلسة، وحجم السجل. يجب أن يخصص مصفوفة سجل النشاط على الـ heap، ويهيئ جميع العناصر إلى 0، ويطبع "Session <sessionId> started for <username>".
    • مدمر (destructor) يحرر الذاكرة المخصصة ويطبع "Session <sessionId> ended for <username>".
    • دالة logActivity(int activityCode) تخزن رمز النشاط في الموقع التالي المتاح (تتبع الموقع الحالي داخليًا).
    • دالة getActivityCount() تعيد عدد الأنشطة التي تم تسجيلها.
    • دالة getUsername() تعيد اسم المستخدم.
  • main.cpp: توضيح سلوك المدمر في سيناريوهات مختلفة. اقرأ اسم المستخدم وحجم السجل من المدخلات (كل منهما في سطر منفصل)، ثم:
    • أنشئ كتلة نطاق (scope block) باستخدام الأقواس المتعرجة { } وداخلها أنشئ كائن Session مخصص على الـ stack باستخدام اسم المستخدم المدخل، ومعرف الجلسة 101، وحجم السجل المدخل. قم بتسجيل نشاطين بالرمزين 1 و 2، ثم اطبع "Stack session activities: <count>". عندما تنتهي الكتلة، يجب أن يعمل المدمر تلقائيًا.
    • بعد الكتلة، اطبع "Stack session destroyed".
    • أنشئ كائن Session مخصص على الـ heap باستخدام new مع اسم المستخدم "Guest"، ومعرف الجلسة 202، وحجم السجل 3. قم بتسجيل نشاط واحد بالرمز 5، واطبع "Heap session activities: <count>"، ثم قم بحذفه صراحةً باستخدام delete.
    • بعد الحذف، اطبع "Heap session destroyed".

يوضح هذا التحدي الطريقتين الرئيسيتين لاستدعاء المدمرات: تلقائيًا عندما تخرج كائنات الـ stack عن النطاق، ويدويًا عندما تقوم بحذف كائنات الـ heap. يجب أن يحرر كلا المسارين سجل النشاط المخصص ديناميكيًا بشكل صحيح لمنع تسرب الذاكرة (memory leaks).

قم بتضمين ملف الرأس الخاص بك في main.cpp باستخدام #include "Session.h".

ورقة مرجعية

الـ destructor (أو الهادم) هو دالة عضو خاصة تعمل تلقائيًا عند تدمير الكائن. يقوم بتحرير الموارد مثل الذاكرة المخصصة ديناميكيًا، أو مقابض الملفات، أو اتصالات الشبكة.

يمتلك الـ destructor نفس اسم الفئة (class)، مسبوقًا بعلامة المد ~. لا يأخذ أي معاملات وليس له نوع إرجاع:

class FileHandler {
    std::string filename;
    int* buffer;
public:
    FileHandler(std::string name, size_t size) 
        : filename(name), buffer(new int[size]) {
        std::cout << "Opening " << filename << "\n";
    }
    
    ~FileHandler() {
        delete[] buffer;
        std::cout << "Closing " << filename << "\n";
    }
};

يتم استدعاء الـ destructors تلقائيًا عندما:

  • تخرج كائنات المكدس (Stack objects) عن النطاق
  • يتم استدعاء delete على كائن مخصص في الذاكرة الحرة (heap)
  • تنتهي دورة حياة كائن مؤقت
void example() {
    FileHandler f1("data.txt", 100);    // تم استدعاء المنشئ (Constructor)
    
    FileHandler* f2 = new FileHandler("log.txt", 50);
    delete f2;                           // تم استدعاء الـ destructor لـ f2
    
}   // تم استدعاء الـ destructor لـ f1 (خرج عن النطاق)

يمكن للفئة أن تمتلك destructor واحد فقط (لا يوجد تحميل زائد). إذا لم تقم بتعريف واحد، فسيقوم المترجم بإنشاء destructor افتراضي. بالنسبة للفئات التي تدير الموارد، يجب عليك كتابة الـ destructor الخاص بك لمنع تسرب الذاكرة.

جرّب بنفسك

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

using namespace std;

int main() {
    // قراءة المدخلات
    string username;
    int logSize;
    getline(cin, username);
    cin >> logSize;
    
    // TODO: قم بإنشاء كتلة نطاق (scope block) باستخدام الأقواس المتعرجة { }
    // داخل الكتلة:
    //   - قم بإنشاء Session مخصص على الـ stack باستخدام username المدخل، و session ID بقيمة 101، وحجم السجل (log size) المدخل
    //   - قم بتسجيل نشاطين بالرمزين 1 و 2
    //   - اطبع "Stack session activities: <count>"
    // عندما تنتهي الكتلة، يتم تشغيل الـ destructor تلقائياً
    
    // TODO: بعد الكتلة، اطبع "Stack session destroyed"
    
    // TODO: قم بإنشاء Session مخصص على الـ heap باستخدام new
    //   - Username: "Guest"، و session ID: 202، و log size: 3
    //   - قم بتسجيل نشاط واحد بالرمز 5
    //   - اطبع "Heap session activities: <count>"
    //   - احذف الـ session
    
    // TODO: بعد الحذف، اطبع "Heap session destroyed"
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming