C++:析构函数

一、什么是析构函数?

  • 析构函数是类的一种特殊成员函数,在对象销毁时自动调用,用于释放对象占用的资源(如堆内存、文件句柄、网络连接等)。
  • 析构函数与构造函数相对,相当于C语言中我们写的free。
cpp 复制代码
class Student {
public:
    // 构造函数
    Student() { cout << "对象诞生" << endl; }
    // 析构函数
    ~Student() { cout << "对象销毁" << endl; }
};

二、语法规则

规则 说明
函数名 ~类名
返回值 无(与构造函数一样)
参数 无(不能重载,一个类只有一个析构函数)
调用 对象生命周期结束时自动调用
cpp 复制代码
class Example {
public:
    ~Example() { }   // 正确
    // ~Example(int x) { }  // 错误:不能有参数
    // void ~Example() { }    // 错误:不能有返回值
};

三、析构函数的作用

  • 清理资源,防止内存泄漏。
cpp 复制代码
class Array {
    int* data;
public:
    Array(int size) {
        data = new int[size];   // 分配堆内存
    }
    ~Array() {
        delete[] data;          // 释放内存
    }
};
其他常见用途:

关闭文件:fclose(fp)

释放互斥锁:unlock()

断开网络连接:close(socket)

四、析构函数何时被调用?

对象类型 调用时机
局部对象(栈) 离开作用域时(如函数结束)
全局/静态对象 程序结束时
堆对象(new) 手动 delete 时
成员对象 包含它的对象销毁时自动调用其析构
cpp 复制代码
void func() {
    Student s;      // 构造
}                   // 离开作用域,自动析构

int main() {
    Student* p = new Student;
    delete p;       // 必须delete,否则析构不调用 → 内存泄漏
}

五、编译器自动生成的析构函数

  • 如果不写析构函数,编译器会生成一个默认析构函数:
    • 对于内置类型成员(int, char*, 指针等):什么都不做。
    • 对于类类型成员(string, vector等):自动调用它们各自的析构函数。
cpp 复制代码
class A {
public:
    ~A() { cout << "A的析构" << endl; }
};

class B {
    int x;    // 内置类型,忽略(看编译器怎么搞)
    A a;      // 类类型,自动调用A的析构
};  // 默认析构函数会调用 a.~A()

六、什么时候必须自定义析构函数?

情况 需要自定义
有动态分配的内存(new) ✅ 必须(释放内存)
持有系统资源(文件、锁、套接字) ✅ 必须
成员对象需要特殊清理 ❌ 通常不需要(它们的析构函数会自动调用)
没有动态资源 ❌ 可以不写,用默认即可
  • 总结:如果类中申请了资源,就必须显示写析构函数,否则会造成资源泄露。
cpp 复制代码
class MyString {
    char* buf;
public:
    MyString(const char* s) {
        buf = new char[strlen(s) + 1];
        strcpy(buf, s);
    }
    ~MyString() {
        delete[] buf;   // 必须手动释放
    }
};

七、完整示例

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

class Book {
    char* title;
public:
    Book(const char* t) {
        title = new char[strlen(t) + 1];
        strcpy(title, t);
        cout << "《" << title << "》上架" << endl;
    }
    ~Book() {
        cout << "《" << title << "》下架" << endl;
        delete[] title;
    }
};

int main() {
    Book b1("C++教程");
    Book* b2 = new Book("深入C++");
    delete b2;   // 手动释放堆对象
    return 0;
}  // b1自动析构

输出:

text

《C++教程》上架

《深入C++》上架

《深入C++》下架

《C++教程》下架

八、补充

基类的析构函数应设为虚函数
  • 如果类会被继承,将析构函数声明为 virtual,否则通过基类指针删除派生类对象时,可能只调用基类析构,导致派生类资源未释放。
cpp 复制代码
class Base {
public:
    virtual ~Base() { }   // 正确做法
};
不要在析构函数中抛出异常
  • 析构函数中抛出异常可能导致程序崩溃或资源泄漏。应捕获所有异常。
cpp 复制代码
~MyClass() {
    try { /* 可能异常的清理 */ }
    catch (...) { /* 记录日志,不抛出 */ }
}
析构函数可以显式调用(不推荐)
  • obj.~MyClass(); 之后不能再使用该对象,通常只在特殊场景(如内存池)中使用。
后定义的先析构
  • 一个局部域的多个对象,C++规定后定义的先析构。
类类型成员会自动调用析构
  • 若我们显示写了析构,对于自定义类型的成员也会调用他的析构,即自定义类型的成员无论什么时候都会自动调用析构函数。
相关推荐
汉克老师1 小时前
CSP-J 2026 初赛试题解析(第一部分:选择题(8-15))精讲
c++·csp-j·小学生·学c++编程
wabs6662 小时前
关于二叉树【力扣572.另一棵树的子树的思考】
数据结构·c++·算法·leetcode·二叉树
风合星语3 小时前
2026 ROS 2 Lyrical C++ 入门(五):Action 实战——任务反馈、取消与超时处理
c++·机器人·ros2·异步编程·lyrical
91刘仁德3 小时前
C++ 继承和多态 设计模式
c语言·c++·笔记
库玛西4 小时前
哈夫曼树与前缀编码:数据压缩的贪心核心
c语言·c++·笔记·考研
别动我齐刘海4 小时前
ROS2 Jazzy + C++ 实战路线——进阶学习3
c++·人工智能·vscode·python·算法·机器学习·机器人
0+1114 小时前
算法 --滑动窗口
c++·算法·leetcode
stolentime4 小时前
(有原题)CSP-S2026第一轮试题(附答案解析、markdown源码)
c++·csp
m0_734571765 小时前
深入理解C++ RAII
开发语言·c++
hetao17338375 小时前
2026-09-18 hetao1733837 的刷题记录
c++·算法