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++规定后定义的先析构。
类类型成员会自动调用析构
  • 若我们显示写了析构,对于自定义类型的成员也会调用他的析构,即自定义类型的成员无论什么时候都会自动调用析构函数。
相关推荐
见过夏天10 小时前
C++ 基础入门完全指南
c++
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境3 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境3 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴4 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境6 天前
C++ 的Eigen 库全解析
c++
卷无止境6 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴6 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake
博客18008 天前
酷宝的使用方法,超好用的免费界面库,C++、MFC可用
c++·mfc·界面库·库来帮·酷宝