析构函数模式
本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 13 课,共 61 课。
每个通过 malloc 创建的对象最终都必须释放。就像我们编写 create_ 函数来处理分配一样,我们也编写 free_ 函数来处理清理。这就是析构函数模式。
关键点是,释放必须按照分配的相反顺序进行。如果你的构造函数先分配内部成员,然后再分配结构体本身,那么析构函数必须先释放结构体的成员,然后再释放结构体:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // 安全检查
free(p->name); // 先释放内部成员
free(p); // 再释放结构体本身
}
开头的 NULL 检查可以防止有人意外传入无效指针时发生崩溃。释放内部的 name 字符串后,我们再释放结构体本身。如果先释放 p,我们就无法再访问 p->name,从而导致内存泄漏。
析构函数的使用很简单:
Person *alice = create_person("Alice", 30);
// ……使用 alice……
free_person(alice); // 用完后清理资源
这种模式可以确保正确释放所有内存。否则,你创建的每个对象都会造成内存泄漏,直到程序耗尽内存。
挑战
简单让我们构建一个 Movie 模块,演示对象的完整生命周期,包括创建和销毁。你将练习编写析构函数,按正确顺序妥善清理所有已分配的内存。
你将创建 3 个文件:
movie.h:声明一个包含两个成员的Movie结构体:char *title(动态分配的字符串)和int year。声明构造函数create_movie,它接收标题和年份;还要声明析构函数free_movie,它接收一个 Movie 指针。使用符号MOVIE_H作为包含保护宏。movie.c:实现这两个函数。构造函数应为结构体及其标题字符串分配内存(别忘了空终止符)。析构函数必须按分配顺序的相反顺序释放内存:先释放内部的title字符串,再释放结构体本身。为确保安全,在析构函数开头检查 NULL。main.c:创建一部电影,显示其信息,然后妥善清理内存。
你将收到两个输入:电影标题(字符串)和上映年份(整数)。
在主文件中,使用 create_movie 创建一部新电影,打印其信息,然后调用 free_movie 释放所有内存。释放内存后,打印一条确认消息。
按以下格式打印输出:
Movie: {title} ({year})
Memory freed例如,输入 Inception 和 2010 时,输出如下:
Movie: Inception (2010)
Memory freed记住:正确编写析构函数的关键是按相反顺序释放内存。构造函数最先分配的内容最后释放,最后分配的内容最先释放。
亲手试试
#include <stdio.h>
#include <stdlib.h>
#include "movie.h"
int main() {
char title[100];
int year;
// 读取输入
scanf("%[^\n]", title);
scanf("%d", &year);
// TODO: 使用 create_movie 创建电影
// TODO: 按以下格式打印电影信息:
// Movie: {title} ({year})
// TODO: 使用 free_movie 释放电影占用的内存
// TODO: 打印 "Memory freed"
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C 在线编译器