VERIFY试验中结构体浅拷贝导致指针未同步置空问题求助
解决C++结构体浅拷贝导致的双删与野指针问题
嘿,我来帮你搞定这个坑!你遇到的正是C++里经典的浅拷贝陷阱——默认的赋值运算符和拷贝构造函数只会逐成员复制指针的地址,而不是复制指针指向的堆内存内容,这就导致两个Verify对象共享同一块内存,析构时会重复释放,还会留下野指针,完全不符合你预期的行为。
问题根源分析
当你执行hispoint = mypoint;时,默认赋值运算符直接把mypoint.ptr的内存地址拷贝给了hispoint.ptr,两个指针指向同一块new int(15)的堆内存。
当main函数作用域结束时:
- 先析构
hispoint:调用DEL函数释放堆内存,并把hispoint.ptr置空,但这不会影响mypoint.ptr的值——它还是原来的内存地址,现在已经变成了野指针(指向已释放的内存)。 - 接着析构
mypoint:DEL函数检测到mypoint.ptr不为空,试图再次释放已经被释放的内存,直接触发未定义行为(通常是程序崩溃或内存错误)。
三种可行解决方案
1. 手动实现深拷贝的拷贝构造与赋值运算符
如果必须使用裸指针,就需要自己实现深拷贝逻辑,让每个对象拥有独立的堆内存:
#include "stdafx.h" #include "Precompiled.h" #include <crtdbg.h> #define ASSERT _ASSERTE #ifdef _DEBUG #define VERIFY ASSERT #else #define VERIFY(expression) (expression) #endif struct Verify { int* ptr; // 默认构造函数,初始化指针为空 Verify() : ptr(nullptr) {} // 拷贝构造函数:深拷贝对方的内存 Verify(const Verify& other) { if (other.ptr != nullptr) { ptr = new int(*other.ptr); // 复制指针指向的内容,而非地址 } else { ptr = nullptr; } } // 赋值运算符:深拷贝+自赋值保护 Verify& operator=(const Verify& other) { if (this != &other) { // 防止自赋值导致的内存泄漏 // 先释放当前对象的内存 if (ptr != nullptr) { delete ptr; ptr = nullptr; } // 深拷贝对方的内容 if (other.ptr != nullptr) { ptr = new int(*other.ptr); } } return *this; } ~Verify() { VERIFY(DEL(&ptr)); } bool DEL(int** p) { if (*p != nullptr) { delete *p; *p = nullptr; return true; } else { return false; } } }; auto main() -> int { Verify mypoint; Verify hispoint; mypoint.ptr = new int(15); hispoint = mypoint; // 现在是深拷贝,两个对象各有独立的内存 }
2. 使用智能指针(推荐的现代C++方式)
用std::unique_ptr或std::shared_ptr代替裸指针,智能指针会自动管理内存,从根源上避免浅拷贝问题:
#include "stdafx.h" #include "Precompiled.h" #include <crtdbg.h> #include <memory> // 必须包含的头文件 #define ASSERT _ASSERTE #ifdef _DEBUG #define VERIFY ASSERT #else #define VERIFY(expression) (expression) #endif struct Verify { // 使用独占所有权的unique_ptr,禁止拷贝,只能移动 std::unique_ptr<int> ptr; ~Verify() { // 无需手动写DEL,unique_ptr会自动释放内存 VERIFY(ptr == nullptr || true); } }; auto main() -> int { Verify mypoint; Verify hispoint; mypoint.ptr = std::make_unique<int>(15); // 用std::move转移所有权,此时mypoint.ptr变为nullptr,hispoint拥有内存 hispoint.ptr = std::move(mypoint.ptr); }
如果需要多个对象共享内存,可以把std::unique_ptr换成std::shared_ptr,它会通过引用计数管理内存释放时机。
3. 禁用拷贝与赋值(如果不需要拷贝功能)
如果你的Verify对象本来就不应该被拷贝,可以直接禁用拷贝构造函数和赋值运算符,编译器会提前报错阻止错误操作:
struct Verify { int* ptr; // 显式禁用拷贝和赋值 Verify(const Verify&) = delete; Verify& operator=(const Verify&) = delete; ~Verify() { VERIFY(DEL(&ptr)); } bool DEL(int** p) { if (*p != nullptr) { delete *p; *p = nullptr; return true; } else { return false; } } };
此时如果再写hispoint = mypoint;,编译器会直接抛出错误,避免你不小心触发浅拷贝。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

