העתקה עמוקה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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;
}
כעת לכל אובייקט יש מחרוזת משלו. אפשר לשנות או לשחרר אחת בלי להשפיע על האחרת. התבנית פשוטה: עבור כל איבר מצביע, מקצים זיכרון חדש ומעתיקים את התוכן, ולא רק את הכתובת.
אתגר
קלבואו נבנה מודול 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;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4כימוס
המושג של מצביעים אטומיםהגדרת מבנים אטומיםפונקציות Get ו-Setאימות ב-Settersסיכום: הקופסה הסודית2אובייקטים ומתודות
מבנים כאובייקטיםמצביע 'Self'נכונות constמצביע לעומת ערךמתודות עזרחזרה: מנהל נקודות5פרויקט: חשבון בנק פשוט
הגדרת הפרויקטמימוש החשבוןתרגלו בעצמכם: קומפיילר C אונליין