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

منشئ النسخ

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

يُنشئ منشئ النسخ كائنًا جديدًا كنسخة من كائن موجود. ويُستدعى عند تهيئة كائن من كائن آخر من النوع نفسه، أو تمرير كائن حسب القيمة، أو إرجاع كائن حسب القيمة.

يأخذ مُنشئ النسخ مرجعًا ثابتًا إلى كائن من الفئة نفسها:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // منشئ النسخ
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // تم استدعاء منشئ النسخ
Player another(original);  // يستدعي أيضًا منشئ النسخ

إذا لم تقم بتعريف copy constructor، ينشئ المصرّف واحدًا يُجري نسخًا سطحيًا - إذ ينسخ قيمة كل عضو مباشرةً. يعمل هذا بشكل جيد مع الأنواع البسيطة، لكنه يسبب مشكلات عندما تدير class الخاصة بك ذاكرةً ديناميكية.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // منشئ النسخ العميق
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // تخصيص ذاكرة جديدة
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // نسخ المحتويات
    }
    
    ~Buffer() { delete[] data; }
};

من دون منشئ copy مخصص، كان both الكائنين سيشيران إلى الذاكرة نفسها. عند تدمير أحدهما، كان لدى الآخر مؤشر متدلٍّ. تعمل نسخة عميقة على تخصيص ذاكرة جديدة ونسخ البيانات الفعلية، مما يضمن امتلاك كل كائن لموارده الخاصة.

challenge icon

التحدي

سهل

لنبنِ نظامًا لسجل الرسائل يوضّح أهمية النسخ العميق عندما تدير فئتك ذاكرة ديناميكية. ستنشئ فئة MessageLog تخزّن الرسائل في مصفوفة مخصّصة ديناميكيًا، وتنفّذ مُنشئ نسخ مناسبًا لضمان امتلاك كل نسخة لذاكرة مستقلة خاصة بها.

ستنشئ ملفين لتنظيم التعليمات البرمجية:

  • MessageLog.h: عرّف فئة MessageLog تدير مجموعة من الرسائل باستخدام الذاكرة الديناميكية. يجب أن تحتوي فئتك على:
    • أعضاء خاصة: مؤشر إلى مصفوفة من السلاسل النصية لتخزين الرسائل، وcapacity للعدد الأقصى من الرسائل، وcount للعدد الحالي من الرسائل
    • مُنشئ مُعلَّم بالمعاملات يأخذ سعة، ويخصّص المصفوفة على الكومة، ويهيّئ count إلى 0
    • مُنشئ نسخ ينفّذ نسخًا عميقًا: تخصيص ذاكرة جديدة ونسخ جميع الرسائل من الكائن المصدر
    • مُدمّر يحرّر الذاكرة المخصّصة ويطبع "MessageLog destroyed"
    • طريقة addMessage(string msg) تضيف رسالة إذا توفّر مكان
    • طريقة getCount() تُرجع العدد الحالي للرسائل
    • طريقة getMessage(int index) تُرجع الرسالة الموجودة عند الفهرس المحدد
  • main.cpp: وضّح أن مُنشئ النسخ ينشئ نسخة مستقلة. اقرأ قيمة السعة ورسالتين من الإدخال، ثم:
    • أنشئ كائن MessageLog باسم original باستخدام السعة المُدخلة
    • أضف كلتا الرسالتين إلى original
    • أنشئ نسخة باسم backup باستخدام مُنشئ النسخ
    • أضف الرسالة الثالثة "New message" إلى original فقط
    • اطبع "Original count: <count>"
    • اطبع "Backup count: <count>"
    • اطبع جميع الرسائل من backup، رسالة واحدة في كل سطر

سيكون تنسيق الإدخال كما يلي:

  • السطر الأول: السعة (عدد صحيح)
  • السطر الثاني: الرسالة الأولى (سلسلة نصية)
  • السطر الثالث: الرسالة الثانية (سلسلة نصية)

إذا نُفّذ النسخ العميق بشكل صحيح، فيجب أن يحتوي backup على رسالتين، بينما يحتوي original على 3 رسائل: مما يثبت امتلاكهما لذاكرة مستقلة. من دون مُنشئ نسخ مناسب، سيشتركان في المصفوفة نفسها، وستؤثر التغييرات في أحدهما على الآخر!

ضمّن ملف الترويسة في main.cpp باستخدام #include "MessageLog.h".

جرّب بنفسك

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // قراءة المدخلات
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: أنشئ MessageLog يسمى 'original' بسعة الإدخال

    // TODO: أضف كلتا الرسالتين إلى original

    // TODO: أنشئ نسخة تسمى 'backup' باستخدام copy constructor

    // TODO: أضف "New message" إلى original فقط

    // TODO: Print "Original count: <count>"

    // TODO: اطبع "Backup count: <count>"

    // TODO: اطبع جميع الرسائل من backup، واحدة في كل سطر

    return 0;
}
quiz iconاختبر نفسك

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

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

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