C++类对象赋值时析构函数调用及double free问题咨询
问题核心原因
你自定义的类仅实现了构造函数、析构函数和浅拷贝的拷贝构造函数,未自定义拷贝赋值运算符,C++编译器会自动生成默认的逐位拷贝版本的赋值运算符,直接复制指针本身而非指针指向的资源,最终导致浅拷贝、资源泄漏、重复释放的问题,完全符合三五法则的适用场景(类自定义了析构函数就需要配套实现拷贝/移动相关的特殊成员函数)。
逐行执行流程对应你的输出
你观察到的现象和输出完全匹配如下执行逻辑:
- 执行
TestClass foo;:调用默认构造函数,输出Creating,foo的some_data指针指向第一块申请的堆内存,随后输出Created once。 - 执行
foo = TestClass();:- 首先生成一个临时的
TestClass匿名对象,调用默认构造函数,输出第二个Creating,临时对象的some_data指向第二块申请的堆内存。 - 调用编译器生成的默认拷贝赋值运算符,直接将临时对象的
some_data指针值复制给foo,foo原本持有的第一块内存的指针被直接覆盖,没有被释放,这就是你观察到的「原对象资源从未被释放」的原因。 - 赋值操作完成后,临时对象的生命周期结束,触发析构函数,输出第一个
Deconstructing,将临时对象持有的第二块内存释放。此时foo的some_data已经指向这块被释放的内存,成为野指针。
- 首先生成一个临时的
- 执行到输出
Created twice后,main函数执行结束,foo的生命周期结束,触发析构函数,输出第二个Deconstructing,尝试释放已经被释放的第二块内存,触发double free报错,程序崩溃。
修复方案
方案1:实现深拷贝语义(需要对象可拷贝时使用)
补充自定义拷贝赋值运算符,先释放自身原有资源,再深拷贝目标对象的资源,同时修改原有浅拷贝的拷贝构造函数为深拷贝实现:
// 修改拷贝构造为深拷贝 TestClass(const TestClass& t) { std::cout << "Copy" << std::endl; some_data = (int*)malloc(10*sizeof(int)); memcpy(some_data, t.some_data, 10*sizeof(int)); } // 新增拷贝赋值运算符 TestClass& operator=(const TestClass& t) { if (this == &t) return *this; // 处理自赋值场景 // 先释放自身原有资源 free(some_data); std::cout << "Copy assign" << std::endl; // 深拷贝目标对象的资源 some_data = (int*)malloc(10*sizeof(int)); memcpy(some_data, t.some_data, 10*sizeof(int)); return *this; }
修改后赋值操作会先释放foo原有的第一块内存,再拷贝临时对象的资源,临时对象析构释放的是自己的资源,foo持有的是独立的新资源,程序结束时释放不会触发重复释放。
方案2:禁用拷贝语义(不需要对象可拷贝时使用)
如果你的类不需要支持拷贝操作,可以直接禁用拷贝构造和拷贝赋值,编译期就会阻止非法的赋值操作,避免运行时错误:
TestClass(const TestClass&) = delete; TestClass& operator=(const TestClass&) = delete;
内容的提问来源于stack exchange,提问作者Matthew
相关产品推荐
相关产品推荐

