深拷贝
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 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 模块,用来演示深拷贝模式。你将创建一个克隆函数,生成一个完全独立的学生对象副本:修改或释放原对象都不会影响克隆对象。
你将创建 3 个文件:
student.h:声明一个Student结构体,包含两个成员:char *name(动态分配的字符串)和int grade。声明 3 个函数:create_student:构造函数,接收姓名和成绩,并返回指向新Student的指针clone_student:接收一个const Student *,并返回指向全新独立副本的指针free_student:析构函数,用于正确释放所有已分配的内存
STUDENT_H添加头文件保护。student.c:实现这 3 个函数。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;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器