如何为std::unique_ptr实现类似weak_ptr的指针,避免双free问题
问题:如何在编译阶段阻止unique_ptr场景下手动delete导致的双free错误
给定以下存在错误的代码,运行后会产生无效输出并触发双free错误:
#include <memory> #include <iostream> class A { public: int i = 8; }; class B { public: std::unique_ptr<A> p; }; int main() { B b; b.p.reset(new A); std::cout << "Out1: " << b.p->i << std::endl; const A* const a = b.p.get(); delete a; // 如何禁止这条语句? std::cout << "Out2: " << b.p->i << std::endl; }
运行输出:
Out1: 8 Out2: 1587634531 free(): double free detected in tcache 2 Aborted
需求是:如何声明指针a,使其拥有对*a(A类型)的读写权限,但不会破坏unique_ptr对象B.p?const修饰指针并非解决方案。另外,std::shared_ptr有对应的std::weak_ptr,那么std::unique_ptr是否有类似机制?
解答
1. std::unique_ptr没有对应的弱引用机制
std::unique_ptr的核心设计是独占对象所有权,它不需要处理多所有权共享的场景,因此没有配套的weak_ptr类。
2. 编译阶段阻止手动delete的可行方案
要实现“能读写对象但无法手动delete”的目标,核心是让外部无法拿到可被delete的原始指针,或通过语法限制delete操作:
方案一:返回对象引用而非指针
直接获取A类型的引用,引用本身不支持delete操作,且能正常读写对象成员:
// 替换原代码中的const A* const a = b.p.get(); A& a = *b.p;
引用的语义天然是“借用”而非“拥有”,能明确传达意图,逻辑上可避免手动delete操作(语法上delete &a虽合法,但不符合引用使用规范,开发者不会这么写)。
方案二:封装类B的访问接口
在类B中提供获取对象的成员函数,返回引用而非原始指针,从根源上隐藏原始指针:
class B { public: std::unique_ptr<A> p; // 提供可读写的引用 A& getA() { return *p; } // 提供只读的const引用 const A& getA() const { return *p; } }; // main中使用 A& a = b.getA();
这样外部完全接触不到unique_ptr持有的原始指针,彻底杜绝手动delete的可能。
方案三:自定义非拥有指针类型(可选)
如果一定要用指针形式,可以封装一个简单的非拥有指针类,禁用转换为原始指针的操作,让delete无法编译:
template<typename T> class non_owning_ptr { public: explicit non_owning_ptr(T* ptr) : m_ptr(ptr) {} // 支持箭头和解引用操作,正常访问对象 T* operator->() { return m_ptr; } T& operator*() { return *m_ptr; } // 禁止转换为可被delete的原始指针 operator T*() = delete; private: T* m_ptr; }; // main中使用 non_owning_ptr<A> a(b.p.get()); // 此时尝试delete a会直接编译报错
为什么const修饰指针没用?
const A* const a只是限制了指针本身不能被修改,以及不能通过指针修改指向的对象,但指针依然是原始指针类型,编译器不会阻止对它执行delete操作,因此无法解决双free问题。
内容的提问来源于stack exchange,提问作者Joachim W
相关产品推荐
相关产品推荐

