深入理解C++ 析构函数<七> 显式调用析构函数

在 C++ 中,析构函数通常由编译器在对象生命周期结束时自动调用:

  • 栈对象离开作用域时;

  • 堆对象被 delete 时;

  • 临时对象表达式结束时。

但有时我们需要显式调用析构函数,即手动执行 obj.~Type() 或 ptr->~Type()。这通常发生在对象的内存管理与生命周期管理分离的场景,例如 placement new、自定义容器、内存池等。

1. 显式调用析构函数的语法

cpp

obj.~Type();          // 对栈对象或引用
ptr->~Type();         // 对指针指向的对象
obj.~decltype(obj)(); // 当类型名不方便写出时(C++11 起)
std::destroy_at(ptr); // C++17 提供的更安全的方式

调用后,对象被销毁(其析构函数执行),但内存并不会被释放。如果对象是 new 出来的,还需要手动 operator delete 释放内存;如果是 placement new 在预分配内存上构造的,则只需调用析构函数,内存由外部管理。

2. 关键注意事项

  • 不要对普通栈对象显式调用析构函数:当对象离开作用域时,编译器会自动再次调用析构函数,导致双重析构,这是未定义行为(UB)。

  • 显式调用后,对象生命周期结束:再访问该对象(调用成员函数、读写成员)都是 UB。

  • 显式调用析构函数 ≠ deletedelete 会先调用析构函数,再释放内存;显式调用只销毁对象,不释放内存。

  • 典型安全用法:用 placement new 在已分配的内存上构造对象,之后必须显式调用析构函数来销毁对象,否则对象持有的资源(如动态内存、文件句柄)会泄漏。

#include <iostream>
#include <new>      // placement new
#include <cstddef>  // alignas, size_t

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass 构造函数被调用\n";
    }
    ~MyClass() {
        std::cout << "MyClass 析构函数被调用\n";
    }
    void sayHello() const {
        std::cout << "Hello from MyClass\n";
    }
};

int main() {
    // 1. 准备一块足够大且对齐的原始内存(栈上数组)
    alignas(MyClass) char buffer[sizeof(MyClass)];

    std::cout << "--- 第一次构造 ---\n";
    // 2. 在 buffer 上使用 placement new 构造对象
    MyClass* obj = new (buffer) MyClass();
    obj->sayHello();

    // 3. 显式调用析构函数,销毁对象(但 buffer 内存仍存在)
    obj->~MyClass();

    // 此时不能再使用 obj->sayHello(),因为对象已销毁

    std::cout << "--- 第二次构造 ---\n";
    // 4. 重新在同一块内存上构造新对象
    MyClass* obj2 = new (buffer) MyClass();
    obj2->sayHello();

    // 5. 再次显式析构
    obj2->~MyClass();

    std::cout << "--- 程序结束 ---\n";
    // buffer 是栈数组,离开 main 时自动释放,无需手动释放内存
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值