为什么不能从 unique_ptr 构造weak_ptr?

新手上路,请多包涵

如果我理解正确, weak_ptr 不会增加托管对象的引用计数,因此它不代表所有权。它只是让您访问一个对象,该对象的生命周期由其他人管理。所以我真的不明白为什么 weak_ptr 不能从 unique_ptr 构造,而只能是 shared_ptr

有人可以简要解释一下吗?

原文由 notadam 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 771
2 个回答

std::weak_ptr 不能使用,除非您通过 std::shared_ptr 将其转换为 lock() 。如果标准允许您的建议,则意味着您需要将 std::weak_ptr 转换为 unique 才能使用它,这违反了唯一性(或重新发明 std::shared_ptr

为了说明,看两段代码:

 std::shared_ptr<int> shared = std::make_shared<int>(10);
std::weak_ptr<int> weak(shared);

{
*(weak.lock()) = 20; //OK, the temporary shared_ptr will be destroyed but the pointee-integer still has shared  to keep it alive
}

现在有了你的建议:

 std::unique_ptr<int> unique = std::make_unique<int>(10);
std::weak_ptr<int> weak(unique);

{
*(weak.lock()) = 20; //not OK. the temporary unique_ptr will be destroyed but unique still points at it!
}

话虽如此,您可能会建议只有一个 unique_ptr ,您仍然可以取消引用 weak_ptr (无需创建另一个 unique_ptr 则没有问题) .但是 unique_ptrshared_ptr 之间有什么区别?或者,常规的 unique_ptr 和使用 get 获取的 C 指针有什么区别?

weak_ptr 不适用于“一般非拥有资源”,它有一个非常具体的工作 - weak_ptr shared_ptr 循环指向内存泄漏。其他任何事情都需要用普通的 unique_ptrshared_ptr 来完成。

原文由 David Haim 发布,翻译遵循 CC BY-SA 4.0 许可协议

我通过在单个对象上实现 weak_ptr 的 MWE 向自己展示了这个问题。 (我在 X 上实现它,但是 X 可以是任何可以告诉我们它何时死亡的东西,例如 unique_ptr 带有自定义删除器)。

然而最终的问题是,在某些时候我们需要对弱指针本身进行引用计数,因为虽然 X 不是共享的,但弱指针 是共享 的。这让我们又回到了使用 shared_ptr 的循环。

也许这样做的唯一好处是单一 所有权意图 更明确并且不能被违反,但是, 正如 Stroustrup 所建议并引用了这个答案,这可以通过 using 声明来暗示。

 #include <iostream>
#include <memory>

template<typename T>
struct ControlBlock
{
    T* Target;
    explicit ControlBlock(T* target) : Target(target) {}
};

template<typename T>
struct WeakReference
{
    std::shared_ptr<ControlBlock<T>> ControlBlock;
    T* Get() { return ControlBlock ? ControlBlock->Target : nullptr; }
};

template<typename T>
struct WeakReferenceRoot
{
    WeakReference<T> _weakRef;
    WeakReferenceRoot(T* target) : _weakRef{std::make_shared<ControlBlock<T>>(target)} { }
    const WeakReference<T>& GetReference() { return _weakRef; }
    ~WeakReferenceRoot() { _weakRef.ControlBlock->Target = nullptr; }
};

struct Customer
{
    WeakReferenceRoot<Customer> Weak{this};
};

int main() {
    WeakReference<Customer> someRef;
    std::cout << "BEFORE SCOPE - WEAK REFERENCE IS " << someRef.Get() << "\n";
    {
        Customer obj{};
        someRef = obj.Weak.GetReference();
        std::cout << "IN SCOPE - WEAK REFERENCE IS " << someRef.Get() << "\n";
    }
    std::cout << "OUT OF SCOPE - WEAK REFERENCE IS " << someRef.Get() << "\n";
    return 0;
}

原文由 c z 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题