所有持有所有权的shared_ptr销毁后访问原始指针是否为未定义行为?
问题解答
首先明确:访问curr->data确实属于未定义行为,你测试时能正常访问只是巧合,绝对不能当成安全的依据。
原因分析
你的代码里,randomNode是持有目标节点的shared_ptr。当修改相邻节点的next和prev时,只是让链表不再持有该节点的所有权,但如果randomNode这个shared_ptr还处于有效状态(比如没出作用域、没被重置),节点的引用计数至少为1,内存不会被释放,此时访问curr->data是安全的。
但如果randomNode在链表修改操作后被销毁(比如它是局部变量,之后离开了作用域),该节点的引用计数会降到0,shared_ptr会自动释放节点内存。此时curr就变成了悬空指针,访问它指向的内存就是标准定义的未定义行为——这时内存可能还没被操作系统回收或覆盖,所以你调试时能看到正确数据,但下次运行、换个环境或者代码稍有改动,就可能出现崩溃、数据乱码等不可预测的结果。
另外要注意:用shared_ptr实现双向链表本身存在循环引用风险——每个节点的prev和next都是shared_ptr,如果链表形成闭环,所有节点的引用计数都不会降到0,会直接导致内存泄漏。
正确处理方式
- 确保访问时节点仍被持有:在调用
process(curr->data)之前,保留持有该节点的shared_ptr(比如让randomNode一直存活到process执行完毕),保证节点内存不会被提前释放。 - 改用
weak_ptr避免循环引用:把Node的prev或next其中一个改成weak_ptr,打破循环引用,需要访问相邻节点时通过lock()获取shared_ptr来确保安全。 - 移除节点时先保留所有权:先把节点的
shared_ptr存起来,再修改链表指针:
std::shared_ptr<Node> temp = randomNode; Node* curr = temp.get(); // 修改相邻节点的指针 if(curr->prev) { curr->prev->next = curr->next; } if(curr->next) { curr->next->prev = curr->prev; } process(curr->data); // temp会在作用域结束后自动释放节点(如果没有其他shared_ptr持有它)
内容的提问来源于stack exchange,提问作者nicky eyes
相关产品推荐
相关产品推荐

