Kopia głęboka
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 15 z 61.
Gdy przypisujesz jedną strukturę do drugiej w C, otrzymujesz kopię płytką: wszystkie pola są kopiowane bajt po bajcie. To działa dobrze w przypadku prostych wartości, takich jak liczby całkowite, ale powoduje problem ze wskaźnikami: obie struktury wskazują ostatecznie na tę samą pamięć.
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // Kopia płytka!
// Zarówno alice.name, jak i copy.name wskazują na TEN SAM ciąg znaków
Jeśli zwolnisz jeden z nich, drugi stanie się nieprawidłowy. Jeśli zmodyfikujesz ciąg znaków za pomocą jednego, zmiana będzie widoczna w obu. Rzadko kiedy tego chcesz.
Głęboka kopia przydziela nową pamięć dla składowych będących wskaźnikami i kopiuje rzeczywiste dane:
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;
}
Teraz każdy obiekt ma własny ciąg znaków. Możesz go zmodyfikować lub zwolnić bez wpływu na drugi. Wzorzec jest prosty: dla każdego składowego wskaźnika przydziel nową pamięć i skopiuj zawartość, a nie tylko adres.
Wyzwanie
ŁatwyZbudujmy moduł Student, który demonstruje wzorzec głębokiego kopiowania. Utworzysz funkcję klonującą, która tworzy całkowicie niezależną kopię obiektu studenta: taką, w której modyfikowanie lub zwalnianie oryginału nie ma wpływu na klon.
Utworzysz trzy pliki:
student.h: Zadeklaruj strukturęStudentz dwoma polami:char *name(dynamicznie alokowany ciąg znaków) orazint grade. Zadeklaruj trzy funkcje:create_student: konstruktor, który przyjmuje imię i ocenę oraz zwraca wskaźnik do nowego obiektu Studentclone_student: przyjmujeconst Student *i zwraca wskaźnik do nowej, niezależnej kopiifree_student: destruktor, który prawidłowo zwalnia całą zaalokowaną pamięć
STUDENT_H.student.c: Zaimplementuj wszystkie trzy funkcje. Kluczowa jest funkcjaclone_student. Musi zaalokować nową pamięć zarówno dla struktury, jak i ciągu znaków z imieniem, a następnie skopiować dane samego ciągu (a nie tylko adres wskaźnika). Dzięki temu klon będzie miał własną pamięć, całkowicie oddzieloną od pamięci oryginału.main.c: Utwórz obiekt studenta, sklonuj go, wyświetl informacje o obu obiektach, a następnie prawidłowo zwolnij oba.
Otrzymasz dwa dane wejściowe: imię studenta (ciąg znaków) i ocenę (liczbę całkowitą).
W pliku głównym utwórz obiekt studenta z podanych wartości, a następnie utwórz jego klon. Wypisz informacje o oryginale i klonie, aby sprawdzić, czy zawierają te same dane. Na koniec zwolnij oba obiekty i wypisz potwierdzenie.
Wypisz dane wyjściowe w tym formacie:
Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freedNa przykład dla danych wejściowych Alice i 95 dane wyjściowe będą następujące:
Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freedWzorzec głębokiego kopiowania zapewnia, że każdy obiekt jest naprawdę niezależny: klon ma własny zaalokowany ciąg znaków, a nie współdzielony wskaźnik do pamięci oryginału.
Spróbuj swoich sił
#include <stdio.h>
#include <stdlib.h>
#include "student.h"
int main() {
char name[100];
int grade;
// Wczytaj dane wejściowe
scanf("%s", name);
scanf("%d", &grade);
// TODO: Utwórz studenta o podanym imieniu i ocenie
// TODO: Utwórz kopię studenta
// TODO: Wypisz informacje o oryginalnym studencie
// Format: "Original: {name}, Grade: {grade}"
// TODO: Wypisz informacje o kopii studenta
// Format: "Clone: {name}, Grade: {grade}"
// TODO: Zwolnij pamięć zajmowaną przez obu studentów
// Wypisz potwierdzenie
printf("Both freed\n");
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania modularnego
Pliki nagłówkoweZabezpieczenia przed wielokrotnym dołączeniemPliki źródłoweFunkcje statycznePodsumowanie: kalkulator modularny4Enkapsulacja
Koncepcja niejawnych wskaźnikówDefiniowanie niejawnych strukturGettery i setteryWalidacja w setterachPowtórka: tajemnicze pudełko2Obiekty i metody
Struktury jako obiektyWskaźnik „self”Poprawność constWskaźnik czy wartośćMetody pomocniczePodsumowanie: menedżer punktów5Projekt: Proste konto bankowe
Konfiguracja projektuImplementacja konta8Polimorfizm
Wskaźniki do funkcji w strukturachSymulowanie metodKoncepcja interfejsuImplementowanie interfejsówIteracja polimorficznaPodsumowanie: Greeter11Wzorce projektowe w C
Wzorzec SingletonWzorzec fabrykiWzorzec iteratoraPodsumowanie: fabryka loggera3Cykl życia obiektu
Wzorzec konstruktoraWzorzec destruktoraInicjalizacja na stosieKopia głębokaPodsumowanie: klasa opakowująca ciąg znaków6Dziedziczenie przez kompozycję
Osadzanie strukturZasada pierwszego elementuDostęp do elementów klasy nadrzędnejKonwersja w górę hierarchiiPodsumowanie: hierarchia kształtów9Projekt: Rysownik kształtów
Przegląd projektuImplementacja kołaImplementacja prostokątaZastosowanie polimorfizmuKontener kształtówPoćwicz samodzielnie: Kompilator C online