为什么C++使用赋值运算符时数组未释放且报double free错误
问题根因分析
你遇到的free(): double free detected in tcache 2错误完全是赋值运算符的错误实现导致的,核心问题是你在赋值逻辑中直接拷贝了堆内存指针,让两个独立的类实例共享了同一块堆内存,最终析构时重复释放同一块地址触发报错。
疑问逐一解答
- 新实例的数组为什么没有被释放?
你在赋值运算符中直接执行了arr = other.arr,直接覆盖了左值对象(也就是代码里的m)原本持有的堆内存指针,m初始化时申请的那部分内存的地址直接丢失,没有任何指针能访问到这部分内存,自然也无法被释放,属于内存泄漏。 - 难道第二个实例运行时不该自动释放对应资源吗?
第二个实例n的资源会被自动释放,恰恰是因为它的资源被正常释放了才会触发double free报错。赋值操作后m.arr和n.arr指向完全相同的堆内存地址,main函数退出时栈对象按构造逆序析构:先析构n释放了这块内存,再析构m时又尝试释放已经被释放的同一块内存,就触发了报错。 - 为什么析构函数对n实例没有生效?
析构函数对n完全生效了,报错的原因不是析构没生效,是两个实例共享内存导致重复释放。你可以在析构函数中加打印输出实例的地址和arr的地址,就能清楚看到两个实例的arr值完全相同,各触发一次释放操作。 - 是否需要在赋值运算符中手动释放n的arr?
完全不需要,n的资源由n自己的生命周期管理,赋值运算符只需要负责处理左值对象(也就是=左边的实例)的资源即可,不要越权操作右值对象的内部资源。
修复方案
你需要重新实现赋值运算符,按照深拷贝的逻辑完成赋值:
- 首先判断是否是自赋值,如果是直接返回自身,避免释放自身资源后无法访问右值数据
- 先释放左值对象原本持有的所有堆内存,避免内存泄漏
- 按照拷贝构造的逻辑,重新申请对应大小的内存,再逐元素拷贝右值的数据,不要直接拷贝指针
修正后的赋值运算符代码如下:
spMatrix& operator = (spMatrix const & other){ // 自赋值判断,避免出错 if (this == &other) { return *this; } // 第一步:释放当前对象原本持有的资源 if(arr != NULL){ for (int i = 0; i < N; i++) { if (arr[i] != NULL) delete [] arr[i]; } delete [] arr; } // 第二步:深拷贝右值对象的内容 N = other.N; arr = new double* [N]; for (int i = 0; i < N; ++i) { arr[i] = new double [N]; for (int j = 0; j < N; ++j) { arr[i][j] = other.arr[i][j]; } } return *this; }
如果想写更安全的实现,也可以用拷贝交换(copy-and-swap)的写法,能自动处理自赋值、异常安全的问题,不需要手动写重复的释放和拷贝逻辑。
内容的提问来源于stack exchange,提问作者ezgigokdemir
相关产品推荐
相关产品推荐

