Sao chép sâu
Thuộc phần Lập trình hướng đối tượng trong hành trình C của Coddy. Bài học 15 trên 61.
Khi gán một struct cho một struct khác trong C, bạn sẽ có một bản sao nông: mọi trường đều được sao chép nguyên vẹn từng byte. Cách này hoạt động tốt với các giá trị đơn giản như số nguyên, nhưng gây ra vấn đề với con trỏ: cả hai struct đều trỏ đến cùng một vùng nhớ.
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // Sao chép nông!
// Cả alice.name và copy.name đều trỏ đến CÙNG MỘT chuỗi
Nếu bạn giải phóng một đối tượng, đối tượng còn lại sẽ không còn hợp lệ. Nếu bạn sửa chuỗi (string) thông qua một đối tượng, cả hai đều thấy thay đổi đó. Đây hiếm khi là điều bạn muốn.
Một bản sao sâu cấp phát bộ nhớ mới cho các thành viên con trỏ và sao chép dữ liệu thực tế:
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;
}
Giờ đây, mỗi đối tượng sở hữu chuỗi riêng. Bạn có thể sửa đổi hoặc giải phóng một chuỗi mà không ảnh hưởng đến chuỗi còn lại. Cách làm rất đơn giản: với mỗi thành viên con trỏ, hãy cấp phát vùng nhớ mới và sao chép nội dung, không chỉ sao chép địa chỉ.
Thử thách
DễHãy xây dựng một module Student minh họa mẫu sao chép sâu. Bạn sẽ tạo một hàm sao chép để tạo ra một bản sao hoàn toàn độc lập của đối tượng sinh viên: khi bạn sửa đổi hoặc giải phóng bản gốc, bản sao vẫn không bị ảnh hưởng.
Bạn sẽ tạo ba tệp:
student.h: Khai báo một structStudentcó hai thành viên:char *name(một chuỗi được cấp phát động) vàint grade. Khai báo ba hàm:create_student: hàm khởi tạo nhận tên và điểm, trả về con trỏ đến một đối tượng Student mớiclone_student: nhận mộtconst Student *và trả về con trỏ đến một bản sao mới, độc lậpfree_student: hàm hủy giải phóng đúng cách toàn bộ bộ nhớ đã cấp phát
STUDENT_H.student.c: Triển khai cả ba hàm. Hàmclone_studentlà phần cốt yếu. Hàm này phải cấp phát bộ nhớ mới cho cả struct lẫn chuỗi tên, sau đó sao chép dữ liệu chuỗi thực tế, không chỉ sao chép địa chỉ con trỏ. Nhờ vậy, bản sao hoàn toàn sở hữu vùng nhớ riêng, tách biệt với bản gốc.main.c: Tạo một sinh viên, sao chép sinh viên đó, hiển thị thông tin của cả hai, rồi dọn dẹp cả hai đối tượng đúng cách.
Bạn sẽ nhận hai đầu vào: tên sinh viên (một chuỗi) và điểm (một số nguyên).
Trong tệp main, hãy tạo một sinh viên bằng các giá trị đã cho, rồi tạo bản sao của sinh viên đó. In thông tin của cả bản gốc lẫn bản sao để xác nhận chúng chứa cùng dữ liệu. Cuối cùng, giải phóng cả hai sinh viên và in thông báo xác nhận.
In đầu ra theo định dạng này:
Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freedVí dụ, với đầu vào Alice và 95, đầu ra sẽ là:
Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freedMẫu sao chép sâu đảm bảo mỗi đối tượng thực sự độc lập: bản sao có chuỗi được cấp phát riêng, không dùng chung con trỏ trỏ đến vùng nhớ của bản gốc.
Tự làm thử
#include <stdio.h>
#include <stdlib.h>
#include "student.h"
int main() {
char name[100];
int grade;
// Đọc đầu vào
scanf("%s", name);
scanf("%d", &grade);
// TODO: Tạo một sinh viên với tên và điểm đã cho
// TODO: Tạo một bản clone của sinh viên
// TODO: In ra thông tin của sinh viên gốc
// Định dạng: "Original: {name}, Grade: {grade}"
// TODO: In ra thông tin của bản clone
// Định dạng: "Clone: {name}, Grade: {grade}"
// TODO: Giải phóng bộ nhớ của cả hai sinh viên
// In ra thông báo xác nhận
printf("Both freed\n");
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình mô-đun
Tệp tiêu đềInclude guardTệp mã nguồnHàm staticÔn tập: Máy tính mô-đun4Tính đóng gói
Khái niệm con trỏ mờĐịnh nghĩa struct mờHàm truy xuất và hàm gánKiểm tra tính hợp lệ trong hàm gánÔn tập: Hộp bí mật2Đối tượng và phương thức
Struct dưới dạng đối tượngCon trỏ 'Self'Tính đúng đắn của constCon trỏ và giá trịPhương thức hỗ trợÔn tập: trình quản lý Point5Dự án: Tài khoản ngân hàng đơn giản
Thiết lập dự ánTriển khai tài khoản3Vòng đời đối tượng
Mẫu hàm khởi tạoMẫu hàm hủyKhởi tạo trên ngăn xếpSao chép sâuÔn tập: lớp bao bọc chuỗi6Kế thừa bằng cách kết hợp
Nhúng structQuy tắc thành viên đầu tiênTruy cập thành viên lớp chaUpcastingÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch C online