النسخ العميق (Deep Copy)
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 15 من 61.
عندما تُسنِد بنيةً إلى أخرى في C، تحصل على نسخة سطحية: يتم نسخ all الحقول بايتًا ببايت. يعمل هذا بشكل جيد مع القيم البسيطة مثل الأعداد الصحيحة، لكنه يسبب مشكلة مع المؤشرات: إذ تنتهي البنيتان إلى الإشارة إلى الذاكرة نفسها.
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // نسخة سطحية!
// كلا من alice.name و copy.name يشيران إلى نفس السلسلة
إذا أجريت free لأحدهما، يصبح الآخر غير صالح. وإذا عدّلت السلسلة النصية من خلال أحدهما، فسيرى كلاهما التغيير. نادرًا ما يكون هذا ما تريده.
تخصّص عملية deep copy ذاكرة جديدة لأعضاء المؤشرات وتنسخ البيانات actual:
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: Declare بنيةStudentذات عضوين:char *name(سلسلة نصية مخصّصة ديناميكيًا) وint grade. Declare ثلاث functions:create_student: constructor يأخذ اسمًا ودرجة، ويعيد مؤشرًا إلى Student جديدclone_student: يأخذconst Student *ويعيد مؤشرًا إلى نسخة جديدة ومستقلةfree_student: destructor يحرّر كل الذاكرة المخصّصة بشكل صحيح
STUDENT_H.student.c: Implement جميع functions الثلاث. إن functionclone_studentهي الجزء الأساسي. يجب أن تخصّص ذاكرة fresh لكل من البنية وسلسلة الاسم، ثم تنسخ بيانات السلسلة الفعلية (وليس عنوان المؤشر فقط). يضمن ذلك امتلاك النسخة المستنسخة لذاكرتها الخاصة، المنفصلة تمامًا عن الكائن الأصلي.main.c: أنشئ طالبًا، واستنسخه، واعرض information كلا الطالبين، ثم نظّف الكائنين بشكل صحيح.
ستتلقى مُدخلين: اسم الطالب (سلسلة نصية) والدرجة (عدد صحيح).
في ملفك الرئيسي، أنشئ طالبًا باستخدام القيم المقدّمة، ثم أنشئ نسخة مستنسخة من ذلك الطالب. اطبع information الكائن الأصلي والنسخة المستنسخة للتحقق من احتوائهما على البيانات نفسها. وأخيرًا، حرّر كلا الطالبين واطبع confirmation.
اطبع الناتج بهذا التنسيق:
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: Print the original student's information
// التنسيق: "Original: {name}, Grade: {grade}"
// TODO: اطبع معلومات النسخة
// التنسيق: "Clone: {name}, Grade: {grade}"
// TODO: حرر كلا الطالبين
// اطبع التأكيد
printf("Both freed\n");
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكالتدرّب بنفسك: مترجم C عبر الإنترنت