【问题标题】:shared_ptr vs CComPtrshared_ptr 与 CComPtr
【发布时间】:2012-03-03 20:26:00
【问题描述】:

我有点习惯于通过 COM 引用计数的概念,而且我对 shared_ptr 有点陌生。 CComPtr 有几个不错的属性,我在 shared_ptr 中找不到,我想知道防止误用 shared_ptr 的模式是什么。

  • AddRef/Release 模式保证每个对象只有一个引用计数(引用计数存储在对象本身上),因此当您有一个随机指针围绕它创建一个 CComPtr 时,它是安全的。另一方面,shared_ptr 有一个单独的 refcount 指针,因此在对象上创建新的 shared_ptr 是不安全的(如果这样做不安全,为什么标准提供了一个在 shared_ptr 上接受 T* 的构造函数?)。这似乎是一个很大的限制,我不明白如何使用 shared_ptrs...

  • 有点极端的情况:我过去用 AddRef/Release 做过的事情:我想要一个包含对 IFoos 的“弱引用”的容器(例如从 URL 到 IConnection 的映射或其他东西)。使用weak_ptr,我可以做到这一点,但我的收藏不会“自行清理”,我会在其中包含过期的指针。使用 Release,我可以实现自己的弱指针(一些工作),它实际上会清理集合。 shared/weak_ptr 有替代方案吗?

  • 直观地说,与只执行一次的 IUnknown 世界相比,执行两次内存分配来创建对象(一个用于引用计数,一个用于对象)会降低性能。访问对象时也存在局部性损失(假设 AddRef 之后经常读取对象的内容,这似乎很可能)。是否比较了两种方法的成本?

【问题讨论】:

  • 如果您使用make_shared,则不会产生两次分配。否则,请发布您的实际用例,因为很难凭空争论。 shared_ptr 很不错,但不是所有情况的唯一解决方案。
  • @KerrekSB:请注意,make_shared 不需要这样做。所有实现都会这样做,但这不是严格要求。
  • 与 CComPtr 最大的不同是这个类不需要存储引用计数。它保存在对象中。也不必担心线程,对性能很重要。

标签: c++ com shared-ptr


【解决方案1】:

如果这样做不安全,为什么标准提供了一个在 shared_ptr 上接受 T* 的构造函数?

因为这是拥有shared_ptrs 而不会打扰的唯一方法。您可以在任何东西上使用shared_ptr。我什至通过使用删除器对象在 C 接口的对象上使用它们。诸如cairo_t* 之类的东西。这样一来,我就再也不用释放任何东西了。

CComPtr 无法做到这一点;它仅适用于IUnknown 样式的对象。

此外,还有std::make_shared,它直接从对象类型和构造函数的参数创建shared_ptr。所以你甚至看不到指针(它通常在一次分配中分配对象及其引用计数而不是两次)。

带有shared_ptr 的正确C++ 习惯用法非常简单:始终使用make_sharedalloc_shared。如果您不能使用它们,那么正确的习惯用法是将直接裸指针构造函数与new:shared_ptr<T> pVal{new T{...}};(或适当的函数)结合使用创建指针)。永远不要在不知道其来源的指针上使用它。

shared/weak_ptr 有替代方案吗?

不,但如果您愿意,可以使用工具制作一个。除了显而易见的方法(定期运行您的集合并删除死weak_ptrs)之外,您还可以将删除器与shared_ptr 相关联,它将(除了删除指针之外)调用任何清理函数来删除那些weak_ptrs .

直观地说,在创建对象时进行两次内存分配会降低性能

参见上面的make_shared

在访问对象时还有一个局部性惩罚(假设 AddRef 之后经常读取对象的内容,这似乎很可能)。

您无需复制 shared_ptr 即可访问其内容,也无需增加引用计数。

现在,让我们谈谈CComPtr 不能 做的一些事情。这是侵入性的。它不能与任意分配器或删除器一起使用(当它具有侵入性时显然不那么重要)。它不能做指针别名,你有一个对象成员的shared_ptr,但实际的引用计数是它所属的对象。这是一件非常有用的事情。

哦,是的,它不是跨平台的。它不受 COM、IUnknown 和所有 开销的约束。

【讨论】:

  • “总是使用 make_shared 或 alloc_shared”:如果我从不使用它(并且总是使用 make_shared),为什么标准提供了采用 T* 的构造函数?我知道使用 make_shared 可以消除性能损失,这很酷。您是否发现自己创建的类既通过 make_shared 实例化又通过其他方式(比如 unique_ptr)?侵入性限制如何? AddRef/Release 可以做别名(因为你实现了 API,你可以重定向调用)。侵入式引用计数的概念是跨平台的(我的意思是,标准可以采用“CComPtr like”类)
  • @user1204233:你没读过我说的下一句话,“如果你不能使用它们......”?在某些情况下无法使用它们。您应该在可能的情况下使用它们,并在不可能的情况下使用其他东西。至于侵入式引用计数的限制,请告诉我如何在不修改类的情况下将其与cairo_tany 预先存在的类一起使用。必须修改代码以使用智能点是一个限制。不是每个人都拥有他们使用的每一段代码。您可以将shared_ptrvectorstring、iostreams 以及任何任何人在任何地方编写的任何未来类一起使用。
  • @user1204233:“我的意思是,标准可以采用“CComPtr like”类”这不是您的问题。你专门问了CComPtr,所以我专门回答了你CComPtr。如果您将问题修改为更具体地涉及侵入性引用计数,那么我将修改我的答案。并且可能已移至 Programmers.se。
  • 保持冷静,我不是有意要冒犯任何人...什么情况下不能使用make_shared?您可以在添加引用计数的字符串周围放置一个 IString,您现在有了一个可以添加引用/释放的新类。您甚至可以将其模板化为具有 with_ref,这将是一个公开 AddRef/Release 的类。想一想,这是完全相同的模式......除了有一种方法可以围绕现有字符串创建一个新的 shared_ptr (你不应该这样做)但是没有办法在 with_ref(除了向下转换,你不应该这样做:))
  • @user1204233:“什么情况下不能使用make_shared?”这是一个新问题,“IString”等概念也是如此。也许你应该问这个问题;这听起来是个好问题。 Stack Overflow 不是论坛。评论部分不适用于后续问题。如果某个答案让您提出新问题,请使用按钮提问。
猜你喜欢
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 2011-09-27
  • 2010-12-18
  • 1970-01-01
  • 2010-09-24
  • 2012-06-27
  • 2021-08-31
相关资源
最近更新 更多