Menu
CoddyTech

בנאי העתקה

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 20 מתוך 104.

בנאי העתקה יוצר אובייקט חדש כהעתק של אובייקט קיים. הוא נקרא כשמאתחלים אובייקט מאובייקט אחר מאותו סוג, מעבירים אובייקט לפי ערך או מחזירים אובייקט לפי ערך.

בנאי ההעתקה מקבל הפניה קבועה לאובייקט מאותה מחלקה. זו חייבת להיות הפניה: העברת other לפי ערך תדרוש בעצמה העתקה, שתפעיל שוב ושוב את בנאי ההעתקה לנצח. ההפניה היא const כי יצירת עותק לעולם לא אמורה לשנות את המקור:

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);  // גם כאן נקרא בנאי ההעתקה

אם לא תגדיר בנאי העתקה, המהדר ייצור אחד שמבצע העתקה שטחית - הוא מעתיק ישירות את הערך של כל איבר. זה עובד היטב עם טיפוסים פשוטים, אך גורם לבעיות כשהמחלקה שלך מנהלת זיכרון דינמי.

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; }
};

ללא בנאי העתקה מותאם אישית, שני האובייקטים היו מצביעים לאותו אזור בזיכרון. כאשר אחד מהם יושמד, לאובייקט השני יהיה מצביע תלוי. העתקה עמוקה מקצה זיכרון חדש ומעתיקה את הנתונים בפועל, וכך מבטיחה שכל אובייקט מחזיק במשאבים משלו.

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, אחת בכל שורה

פורמט הקלט יהיה:

  • שורה ראשונה: קיבולת (מספר שלם)
  • שורה שנייה: ההודעה הראשונה (מחרוזת)
  • שורה שלישית: ההודעה השנייה (מחרוזת)

אם ההעתקה העמוקה שלך ממומשת כראוי, בעותק הגיבוי אמורות להיות 2 הודעות, בעוד שבמקור יש 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' באמצעות בנאי ההעתקה

    // TODO: הוסף את "New message" ל־original בלבד

    // TODO: הדפס "Original count: <count>"

    // TODO: הדפס "Backup count: <count>"

    // TODO: הדפס את כל ההודעות מ־backup, אחת בכל שורה

    return 0;
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C++ אונליין