C++删除第二个对象时触发崩溃 双重释放问题排查修复
崩溃根因
程序第二次执行delete switch1时崩溃的核心原因是双重内存释放(Double Free):
- 代码中仅通过
new Lamp{numberClick}在堆上创建了1个Lamp实例,该实例的内存地址被同时传入switch0、switch1两个Switch对象,保存在各自的lamp成员指针中 - Switch类的析构函数固定执行
delete lamp;逻辑,没有做对象所有权的区分 - 执行
delete switch0时,switch0的析构函数已经释放了Lamp实例占用的堆内存,此时switch1中存储的lamp指针已经成为指向已回收内存的野指针 - 后续执行
delete switch1时,析构函数再次对已经失效的野指针执行delete操作,直接触发内存访问违规,导致程序崩溃
除了运行时崩溃问题,原代码还存在4处会导致编译失败、逻辑异常的笔误:
Lamp::click()方法关灯分支的计数器拼写错误:conter++应为counter++- main函数创建switch1的语句缺失右括号:
Switch* switch1 = new Switch(lamp末尾需补) - 操作switch0的语句变量名大小写错误:
Switch0->push();应为switch0->push();(C++变量名区分大小写) - 操作switch1的方法调用缺失括号:
switch1->push应为switch1->push();
修正与规避方案
针对这类裸指针共享导致的内存问题,可按以下规则处理:
- 明确对象所有权边界:如果多个对象需要共享同一个堆实例,绝对不要在每个持有指针的对象析构时都执行释放操作,仅由持有实例所有权的模块负责内存回收。本场景中Lamp实例在main函数中创建,所有权归属main函数,应当删除Switch类析构函数中的
delete lamp;逻辑,等两个Switch对象都释放完成后,再在main函数中单独执行delete lamp;修正后的核心逻辑示例:
// Switch类不持有Lamp所有权,移除析构中的delete逻辑 ~Switch() = default; // main函数末尾按创建逆序释放资源 delete switch1; delete switch0; delete lamp; - 优先使用现代C++智能指针替代裸指针:
- 独占资源场景使用
std::unique_ptr,编译期即可阻止所有权拷贝,从语法层面避免重复释放 - 共享资源场景使用
std::shared_ptr,内部通过引用计数自动管理内存,最后一个持有指针的对象销毁时才会释放实例,彻底杜绝双重释放问题
- 独占资源场景使用
- 裸指针场景下做好防御:执行delete操作后立刻将指针赋值为
nullptr,由于C++标准规定delete空指针是无操作的安全行为,可避免野指针误释放 - 配套工具提前排查问题:编译时加上
-Wall -Wextra参数,提前捕获拼写、未初始化等低级错误;测试阶段使用内存检测工具,可精确定位内存重复释放、越界访问等隐蔽问题
内容的提问来源于stack exchange,提问作者Simone Polizzi
相关产品推荐
相关产品推荐

