系列文章目录后续会将类与对象中重要的六个默认成员函数构造、析构、拷贝构造、赋值重载、普通对象和const对象的取地址重载链接放在这目录里有学习需求的可以先点个收藏~文章目录系列文章目录前言一、析构函数核心定义与作用1. 核心定位2. 关键误区纠正必看3. 应用场景区分二、析构函数语法规则1. 基础语法格式2. 七大硬性规则考点汇总规则 1命名固定格式唯一规则 2无返回值、无参数规则 3自动调用禁止手动常规调用规则 4编译器会生成默认析构规则 5自定义析构不会影响成员析构规则 6局部对象析构顺序规则 7资源类必须手动实现析构3. 无资源析构代码示例三、析构函数的四大调用时机场景 1局部栈对象最常用场景 2全局 / 静态对象场景 3堆对象new 创建场景 4临时对象四、默认析构函数实战嵌套类成员1. 代码实现2. 流程解析3. 结论五、有资源析构实战Stack 栈类1. C 语言版本2. C 版本六、析构顺序重点1. 基础规则2. 代码示例3. 嵌套成员析构顺序总结前言在 C 类与对象体系中构造函数负责对象创建时的初始化而析构函数则是对象生命周期的 “收尾者”。二者成对出现是 C 六大默认成员函数中最核心的两组函数。很多初学者会认为析构函数是用来销毁对象本身的内存。但这是个误区对象的栈 / 堆内存由操作系统或new/delete管理析构函数的核心使命是释放对象持有外部资源堆内存、文件句柄、网络套接字等。一、析构函数核心定义与作用1. 核心定位析构函数Destructor是类的特殊成员函数属于 C 六大默认成员函数之一。执行时机对象生命周期结束时系统自动调用无需手动触发核心功能清理对象在运行过程中申请的外部资源堆内存、打开的文件、硬件句柄等对比 C 语言C 语言使用结构体时需要手动编写Destroy销毁函数并主动调用C 依靠析构函数自动完成资源清理彻底避免 “忘记释放资源导致内存泄漏” 的问题。2. 关键误区纠正必看错误认知析构函数负责释放对象本身的内存。正确认知析构函数仅清理对象内部引用的外部资源比如栈类中数组指向的堆内存。解释局部栈对象内存由函数栈帧管理函数结束后栈帧自动回收和析构无关堆对象new创建对象内存由delete释放delete会先调用析构函数再释放内存。3. 应用场景区分无外部资源的类如Date日期类成员均为int内置类型无需手动写析构编译器会默认生成析构函数持有动态内存 / 文件句柄的类如Stack栈类必须手动实现析构函数否则必然造成内存泄漏。二、析构函数语法规则1. 基础语法格式析构函数命名规则类名前加波浪号~格式如下class类名{public:~类名(){// 资源清理逻辑}};2. 七大硬性规则考点汇总规则 1命名固定格式唯一析构函数名必须是~类名波浪号是析构函数的专属标识不能修改。规则 2无返回值、无参数不写返回值连void都不需要C 语法强制规定没有任何形参。延伸推论因为没有参数析构函数无法重载。一个类有且只能有一个析构函数构造函数可以重载这是二者核心区别。规则 3自动调用禁止手动常规调用对象生命周期结束时系统自动执行正常开发中不需要手动调用。语法上虽然支持显式调用但毫无意义且可能引起重复析构问题。规则 4编译器会生成默认析构如果我们没有显式定义析构函数编译器会自动生成一个默认析构函数。默认析构的行为分两种成员处理内置类型成员int、char、指针等默认析构不做任何处理自定义类成员类中嵌套其他类对象自动调用该成员自身的析构函数逐层清理。规则 5自定义析构不会影响成员析构哪怕我们手动实现了析构函数类中的自定义类型成员依然会在当前析构函数执行完毕后自动调用自身析构。规则 6局部对象析构顺序在同一个局部作用域中后定义的对象先析构遵循栈 “后进先出” 的特性。规则 7资源类必须手动实现析构只要类中通过malloc/new/fopen等申请了外部资源必须手写析构函数进行释放资源否则会内存泄漏。3. 无资源析构代码示例Date类成员均为内置int无外部资源使用默认析构即可#includeiostreamusingnamespacestd;classDate{public:// 全缺省构造函数Date(intyear1,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}voidPrint(){cout_year/_month/_dayendl;}// 编译器自动生成默认析构此处省略private:int_year;int_month;int_day;};intmain(){Dated1(2026,6,8);d1.Print();// 函数结束d1生命周期结束自动调用默认析构return0;}三、析构函数的四大调用时机不同存储类型的对象析构函数触发时机完全不同。场景 1局部栈对象最常用触发时机对象所在局部作用域结束函数结束、代码块{}结束自动调用析构。#includeiostreamusingnamespacestd;classTest{public:~Test(){coutTest 析构函数被调用endl;}};voidFunc(){Test t1;// 定义局部对象cout函数Func执行中endl;}// 作用域结束t1销毁 → 调用析构intmain(){Func();coutmain函数继续执行endl;return0;}运行结果场景 2全局 / 静态对象触发时机整个程序正常退出时才会调用析构。全局 / 静态对象生命周期贯穿整个程序函数结束不会触发析构。#includeiostreamusingnamespacestd;classTest{public:~Test(){coutTest 析构函数被调用endl;}};Test g_t;// 全局对象voidFunc(){staticTest s_t;// 静态局部对象cout函数Func执行中endl;}intmain(){Func();coutmain函数执行完毕endl;return0;// 程序退出全局、静态对象依次析构}运行结果场景 3堆对象new 创建触发时机必须手动使用delete释放对象delete会先调用析构函数再释放堆内存。如果只new不delete析构永远不执行造成内存泄漏。#includeiostreamusingnamespacestd;classTest{public:~Test(){coutTest 析构函数被调用endl;}};intmain(){Test*pnewTest;// new创建堆对象仅调用构造cout堆对象使用中endl;deletep;// 先调用析构再释放堆内存pnullptr;return0;}运行结果场景 4临时对象触发时机创建临时对象的完整表达式执行完毕立即调用析构。#includeiostreamusingnamespacestd;classTest{public:Test(){cout构造endl;}~Test(){cout析构endl;}};intmain(){cout开始endl;Test();// 创建临时对象表达式结束立即析构cout结束endl;return0;}运行结果#includeiostreamusingnamespacestd;classTest{public:Test(){cout构造endl;}~Test(){cout析构endl;}};intmain(){cout开始endl;Test();// 创建临时对象表达式结束立即析构cout结束endl;return0;}四、默认析构函数实战嵌套类成员例子用两个栈实现队列MyQueue类包含两个Stack类型的成员对象用来理解自定义成员的析构规则。1. 代码实现#includeiostream#includecstdlibusingnamespacestd;typedefintSTDateType;// 栈类持有堆内存手动实现析构classStack{public:// 全缺省构造默认容量4Stack(intn4){_a(STDateType*)malloc(sizeof(STDateType)*n);if(nullptr_a){perror(malloc 申请空间失败);exit(1);}_capacityn;_top0;}// 手动实现析构释放堆内存~Stack(){cout~Stack() 栈析构endl;free(_a);_anullptr;_capacity0;_top0;}private:STDateType*_a;// 指向堆内存size_t _capacity;size_t _top;};// 队列类嵌套两个Stack成员无自定义析构使用默认析构classMyQueue{private:Stack pushst;// 入栈成员Stack popst;// 出栈成员};intmain(){MyQueue mq;// 创建队列对象return0;// 作用域结束mq析构}2. 流程解析MyQueue没有手动写析构使用编译器默认析构默认析构不会处理内置成员但会依次调用所有自定义成员pushst、popst的析构函数最终两个Stack对象的析构函数被执行堆内存正常释放无内存泄漏。3. 结论嵌套自定义成员的类哪怕不写析构成员的资源也会被自动清理只有当前类自身申请外部资源时才需要手动实现析构。五、有资源析构实战Stack 栈类通过Stack栈类对比C 语言手动销毁和C 析构自动销毁的差异。1. C 语言版本#includestdio.h#includestdlib.htypedefintSTDataType;typedefstructStack{STDataType*_a;size_t_capacity;size_t_top;}ST;// 初始化voidSTInit(ST*st,intn){st-_a(STDataType*)malloc(sizeof(STDataType)*n);st-_capacityn;st-_top0;}// 手动销毁必须主动调用容易遗忘voidSTDestroy(ST*st){free(st-_a);st-_aNULL;}intmain(){ST st;STInit(st,4);// 业务逻辑...STDestroy(st);// 忘记调用会内存泄漏return0;}2. C 版本#includeiostream#includecstdlibusingnamespacestd;typedefintSTDateType;classStack{public:Stack(intn4){_a(STDataType*)malloc(sizeof(STDataType)*n);if(nullptr_a){perror(malloc 申请空间失败);exit(1);}_capacityn;_top0;}// 析构自动释放堆内存无需手动调用~Stack(){free(_a);_anullptr;_capacity0;_top0;}voidPush(STDataType x){_a[_top]x;}private:STDateType*_a;size_t _capacity;size_t _top;};intmain(){Stack st;// 构造初始化st.Push(10);st.Push(20);// 无需手动销毁函数结束自动析构释放内存return0;}六、析构顺序重点1. 基础规则同一局部作用域内的多个对象构造顺序从上到下代码书写顺序析构顺序从下到上后构造的对象先析构本质原因局部对象存储在栈中栈遵循后进先出LIFO规则。2. 代码示例#includeiostreamusingnamespacestd;classTest{public:Test(intid):_id(id){cout构造对象_idendl;}~Test(){cout析构对象_idendl;}private:int_id;};intmain(){Testt1(1);// 第一个构造Testt2(2);// 第二个构造Testt3(3);// 第三个构造cout执行完毕endl;return0;}运行结果可以清晰看到t3最后构造最先析构t1最先构造最后析构。3. 嵌套成员析构顺序对于包含自定义成员的类构造顺序先构造成员 → 再构造当前对象析构顺序先析构当前对象 → 再析构成员逆序总结下一篇讲拷贝构造函数。