为何动态分配内存被多次释放?已用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
问题根源拆解
临时对象析构触发重复销毁循环
在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已经被置空,所以会重复执行创建逻辑,导致多次分配和销毁不同的内存块。
- 临时B对象构造时,检测到
默认拷贝构造的隐性影响
你没有为B类定义拷贝构造函数,编译器会生成默认拷贝构造函数。vector中存储的B对象是通过默认拷贝得到的,这些拷贝对象的构造函数不会被调用(不会触发B的创建逻辑),但它们的析构函数会在main结束时执行——不过此时B::a已经被临时对象的析构置空,所以不会再触发销毁操作。关于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
相关产品推荐
相关产品推荐

