Menu
Coddy logo textTech

העתקה עמוקה

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

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

typedef struct {
    char *name;
    int age;
} Person;

Person alice = {"Alice", 30};
Person copy = alice;  // העתקה רדודה!
// גם alice.name וגם copy.name מצביעים על אותה מחרוזת בדיוק

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

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

Person *clone_person(const Person *original) {
    Person *copy = malloc(sizeof(Person));
    copy->name = malloc(strlen(original->name) + 1);
    strcpy(copy->name, original->name);
    copy->age = original->age;
    return copy;
}

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

challenge icon

אתגר

קל

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

תיצרו שלושה קבצים:

  • student.h: הצהירו על מבנה Student עם שני שדות: char *name (מחרוזת שהוקצתה באופן דינמי) ו-int grade. הצהירו על שלוש פונקציות:
    • create_student: בנאי שמקבל שם וציון ומחזיר מצביע לסטודנט חדש
    • clone_student: מקבלת const Student * ומחזירה מצביע לעותק חדש ועצמאי
    • free_student: מפרקת שמשחררת כראוי את כל הזיכרון שהוקצה
    השתמשו בשומרי הכללה עם הסמל STUDENT_H.
  • student.c: ממשו את כל שלוש הפונקציות. הפונקציה clone_student היא העיקר. עליה להקצות זיכרון חדש הן למבנה והן למחרוזת השם, ואז להעתיק את נתוני המחרוזת עצמם (ולא רק את כתובת המצביע). כך מובטח שהעותק המשוכפל ינהל את הזיכרון שלו בנפרד לחלוטין מהמקור.
  • main.c: צרו סטודנט, שכפלו אותו, הציגו את המידע של שני הסטודנטים, ואז נקו כראוי את שני האובייקטים.

תקבלו שתי קלטים: שם הסטודנט (מחרוזת) ו-הציון (מספר שלם).

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

הדפיסו את הפלט בפורמט הזה:

Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freed

לדוגמה, עם הקלטים Alice ו-95, הפלט יהיה:

Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freed

דפוס ההעתקה העמוקה מבטיח שכל אובייקט יהיה עצמאי באמת: לעותק המשוכפל יש מחרוזת שהוקצתה עבורו, ולא מצביע משותף לזיכרון של המקור.

נסו בעצמכם

#include <stdio.h>
#include <stdlib.h>
#include "student.h"

int main() {
    char name[100];
    int grade;
    
    // קרא את הקלט
    scanf("%s", name);
    scanf("%d", &grade);
    
    // TODO: צור סטודנט עם השם והציון שסופקו
    
    // TODO: צור עותק של הסטודנט
    
    // TODO: הדפס את פרטי הסטודנט המקורי
    // פורמט: "Original: {name}, Grade: {grade}"
    
    // TODO: הדפס את פרטי העותק
    // פורמט: "Clone: {name}, Grade: {grade}"
    
    // TODO: שחרר את הזיכרון של שני הסטודנטים
    
    // הדפס אישור
    printf("Both freed\n");
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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