为什么不能从unique_ptr构造weak_ptr?

时间:2015-03-15 09:58:28

标签: c++ shared-ptr smart-pointers unique-ptr weak-ptr

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

有人能简单解释一下吗?

7 个答案:

答案 0 :(得分:32)

如果您考虑一下,weak_ptr必须引用除对象本身之外的其他内容。那是因为对象可以不再存在(当没有更强的指针时),weak_ptr仍然必须引用包含对象不再存在的信息的东西。

使用shared_ptr,包含引用计数的东西。但是使用unique_ptr时,没有引用计数,因此没有包含引用计数的东西,因此当对象消失时不会继续存在。因此weak_ptr没有任何内容可供参考。

使用这样的weak_ptr也没有明智的方法。要使用它,您必须有一些方法来保证在您使用它时对象没有被破坏。使用shared_ptr这很容易 - 这是shared_ptr的作用。但是你如何用unique_ptr做到这一点?你显然不能拥有其中的两个,而其他东西必须已经拥有该对象,否则它会被破坏,因为你的指针很弱。

答案 1 :(得分:16)

除非您通过std::weak_ptr将其转换为std::shared_ptr,否则无法使用

lock()。如果标准允许你的建议,这意味着你需要将std :: weak_ptr转换为唯一才能使用它,违反唯一性(或重新发明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不是&#34;一般的非常规资源&#34;,它有一个非常具体的工作 - weak_ptr的主要目标是防止shared_ptr的圆形指向哪个会造成内存泄漏。还需要使用普通unique_ptrshared_ptr完成其他任务。

答案 2 :(得分:12)

shared_ptr基本上有两部分:

  1. 指向对象
  2. 引用计数对象
  3. 一旦引用计数降至零,对象(#1)将被删除。

    现在weak_ptr需要知道某个对象是否仍然存在。为了做到这一点,它必须能够看到引用计数对象(#2),如果它不为零,它可以为对象创建shared_ptr(通过递增引用计数)。如果计数为零,则返回空shared_ptr

    现在考虑何时可以删除引用计数对象(#2)?我们必须等到没有shared_ptr OR weak_ptr对象引用它。为此,引用计数对象包含两个引用计数,引用和引用。引用计数对象仅在其计数均为零时才会被删除。这意味着只有在所有弱引用消失后才能释放部分内存(这意味着hidden disadvantage with make_shared)。

    tl; dr; weak_ptr取决于弱引用计数,它是shared_ptr的一部分,不能有weak_ptr没有shared_ptr

答案 3 :(得分:8)

从概念上讲,没有什么能阻止一个实现,其中weak_ptr只提供访问权限,而unique_ptr控制生命周期。但是,有一些问题:

  • unique_ptr开始时不使用引用计数。添加用于管理弱引用的管理结构是可能的,但需要额外的动态分配。由于unique_ptr应该避免原始指针上的任何(!)运行时开销,因此不能接受该开销。
  • 为了使用weak_ptr引用的对象,您需要提取一个真实的&#34;它的引用,它将首先验证指针是否先过期,然后给你这个真正的引用(在这种情况下为shared_ptr)。这意味着您突然有一个对应该唯一拥有的对象的第二个引用,这是一个错误的配方。这不能通过返回一个混合的半强指针来修复,该指针只会暂时延迟对指针的可能破坏,因为你也可以存储那个,同时击败unique_ptr背后的想法。

只是想知道,你在这里使用weak_ptr试图解决什么问题?

答案 4 :(得分:5)

没有人提到问题的性能方面,所以让我把我的0.02美元扔进去。

weak_ptr必须以某种方式知道相应的shared_ptr何时已超出范围且指向的对象已被解除分配和销毁。这意味着shared_ptr需要以某种方式将对每个weak_ptr的破坏传达给同一个对象。这有一定的成本 - 例如,需要更新全局哈希表,其中weak_ptr从中获取地址(如果对象被销毁,则为nullptr。)

这也涉及锁定多线程环境,因此对某些任务来说可能太慢。

但是,unique_ptr的目标是提供零费用 RAII风格的抽象类。因此,它不应该产生任何其他成本,而不是delete(或delete[])动态分配的对象。例如,通过执行锁定或以其他方式保护的哈希表访问所施加的延迟可能与释放的成本相当,但在unique_ptr的情况下这是不可取的。

答案 5 :(得分:3)

看起来每个人都在这里写关于std :: weak_ptr但不是关于弱的概念,我相信是作者所要求的

我认为没有人提到为什么标准库没有为unique_ptr提供weak_ptr。弱指针CONCEPT不会拒绝unique_ptr用法。弱指针只是一个信息,如果该对象已被删除,所以它不是一个神奇但非常简单的广义观察者模式。

这是因为线程安全且与shared_ptr保持一致。

您只是不能保证在尖端对象的调用方法期间不会破坏您的weak_ptr(从其他线程上存在的unique_ptr创建)。 这是因为weak_ptr需要与std :: shared_ptr保持一致,以保证线程安全。您可以实现与unique_ptr一起正常工作的weak_ptr,但只能在同一个线程上 - 在这种情况下不需要锁定方法。您可以检查base :: WeakPtr和base :: WeakPtrFactory的铬源 - 您可以使用unique_ptr自由使用它。 Chromium弱指针代码很可能是基于最后一个成员销毁 - 你需要添加工厂作为最后一个成员,之后我相信WeakPtr被告知对象删除(我不是100%肯定) - 所以它看起来不是很难实现。

总体上使用带有弱指针概念的unique_ptr是可以的恕我直言。

答案 6 :(得分:0)

区分unique_ptr优先于shared_ptr的原因可能很有用。

性能一个明显的原因是计算时间和内存使用。正如当前定义的那样,shared_ptr对象通常需要类似引用计数值的东西,这需要占用空间并且还必须主动维护。 unique_ptr个对象没有。

语义使用unique_ptr,作为程序员,当指向对象将被销毁时,你会有一个很好的主意:当unique_ptr被销毁或当调用其中一个修改方法时。等等大型或不熟悉的代码库,使用unique_ptr静态传达(并强制执行)有关程序运行时行为的一些信息,这些信息可能并不明显。

上述评论通常侧重于基于性能的原因,即weak_ptr个对象与unique_ptr个对象绑定是不可取的。但有人可能想知道基于语义的论证是否足以让STL的某些未来版本支持原始问题隐含的用例。