You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何动态分配内存被多次释放?已用mutex与static变量仍失效

问题:使用mutex和static变量仍出现多次内存释放的原因

我用mutex和static变量保证动态分配的内存只释放一次,但还是出现了多次释放的情况,这是为什么?我知道有观点认为不需要显式释放static指针,但想明确这个问题的原因。以下是我用C++20写的代码和运行结果:

// use C++-20
#include <iostream>
#include <vector>
#include <thread>
#include <atomic>

using namespace std;

class A {

private:
    static atomic_int *a;//=NULL;
    static std::mutex mutex_;
public:
    A() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (!a) {
            a = new atomic_int[10]();
            std::cout << "creation A..." << std::endl;
        }
    }

    ~A() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (a) {
            delete[] a;
            //delete a;
            a = NULL;
            std::cout << "deletion A..." << std::endl;
        }

    }

    A(A &t) {
        cout << "copy..." << endl;
    }


};

atomic_int *A::a = NULL;
std::mutex A::mutex_;

class B {
private:
    static A *a;
    static std::mutex mutex_;

public:
    B() {

        std::lock_guard<std::mutex> lock(mutex_);
        if (!a) {
            a = new A();
            std::cout << "creation B ..." << std::endl;
        }

    }

    ~B() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (a) {
            cout << "Delete B" << endl;
            delete a;
            a = NULL;
        }
    }

    void run() {
        std::cout << "Hello from B" << std::endl;
    }

    thread take_action() {
        return thread([this] { run(); });
    }
};

A *B::a = NULL;
std::mutex B::mutex_;

int main() {
    cout << "Hello World" << endl;

    vector<B> b;
    vector<std::thread> b_thread;

    for (int i = 0; i < 3; ++i) {
        b.push_back(B());

    }

//
//    B b1, b2;
    for (int i = 0; i < 3; ++i) {
        b[i].run();
    }

    for (int i = 0; i < b.size(); ++i) {
        b_thread.push_back(b[i].take_action());

    }

    for (int i = 0; i < b_thread.size(); ++i) {
        b_thread[i].join();
    }

//    b1.run();

//    b2.run();

    return 0;
}

运行结果:

Hello World
creation A...
creation B ...
Delete B
deletion A...
creation A...
creation B ...
Delete B
deletion A...
creation A...
creation B ...
Delete B
deletion A...
Hello from B
Hello from B
Hello from B
Hello from B
Hello from B
Hello from B

问题根源拆解

  1. 临时对象析构触发重复销毁循环
    在main的循环中执行b.push_back(B())时,会先创建临时B对象:

    • 临时B对象构造时,检测到B::a为空,于是创建A实例(进而分配A::a指向的数组),输出创建日志。
    • 临时对象被拷贝到vector后会立即销毁,调用~B():此时B::a不为空,执行delete a触发~A(),删除A::a的数组并置空,同时将B::a置空,输出销毁日志。
    • 下一次循环创建新的临时B对象时,B::a已经被置空,所以会重复执行创建逻辑,导致多次分配和销毁不同的内存块。
  2. 默认拷贝构造的隐性影响
    你没有为B类定义拷贝构造函数,编译器会生成默认拷贝构造函数。vector中存储的B对象是通过默认拷贝得到的,这些拷贝对象的构造函数不会被调用(不会触发B的创建逻辑),但它们的析构函数会在main结束时执行——不过此时B::a已经被临时对象的析构置空,所以不会再触发销毁操作。

  3. 关于static指针的释放
    static指针指向的动态内存,其生命周期覆盖整个程序运行期,即使不显式释放,程序退出时操作系统也会回收这部分内存。你的问题本质不是“是否需要释放”,而是错误地重复触发了创建-销毁的循环,导致多次分配和释放不同的内存块,而非多次释放同一块内存。

修复方案示例

要解决这个问题,可以采用以下两种常见方式:

  • 禁止拷贝,直接在容器中构造对象:
    给B类禁用拷贝构造和赋值操作,改用emplace_back直接在vector中构造B对象,避免临时对象的创建:
    class B {
        // ... 其他代码 ...
    public:
        B(const B&) = delete;
        B& operator=(const B&) = delete;
        // ... 其他代码 ...
    };
    
    // main中修改为:
    for (int i = 0; i < 3; ++i) {
        b.emplace_back();
    }
    
  • 利用静态局部变量管理资源:
    将B的static指针替换为静态局部变量,借助C++11及以后的线程安全特性,自动保证资源只初始化一次,无需手动管理释放:
    class B {
    public:
        B() {
            // 静态局部变量只初始化一次,程序退出时自动析构
            static A a_instance;
        }
        ~B() = default; // 无需手动销毁static资源
        // ... 其他代码 ...
    };
    

内容的提问来源于stack exchange,提问作者qwert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:40:29