首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的侵入性与非侵入性参考指标

C++中的侵入性与非侵入性参考指标
EN

Stack Overflow用户
提问于 2010-03-21 09:14:55
回答 4查看 5.8K关注 0票数 10

在过去的几年里,我普遍认为

如果我要使用参考计数的智能指针

侵入性智能指针是可行的方法。

--

但是,由于以下原因,我开始喜欢非侵入性智能指针:

  1. 我只使用智能指针(所以没有Foo*躺在附近,只有Ptr)
  2. 我开始为每个类构建自定义分配器。(所以Foo将重载操作符)。
  3. 现在,如果Foo有一个所有Ptr的列表(可以很容易地使用非侵入性智能指针)。
  4. 然后,我可以避免内存碎片问题,因为Foo类可以移动对象(只需更新相应的Ptr)。

这个Foo在非侵入性智能指针中移动对象比入侵智能指针更容易的唯一原因是:

在非侵入式智能指针中,只有一个指针指向每个Foo.

在入侵智能指针中,我不知道每个Foo指向多少个对象。

现在,非侵入性智能指针的唯一成本.是双重间接。也许这搞砸了暗藏室。

有没有人能很好地研究这一额外的间接方向是昂贵的?

编辑:通过智能指针,我可能指的是其他人所说的“共享-指针”;整个想法是:有一个引用计数附加到对象,当它点击0时,该对象将自动删除。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-03-21 10:01:01

侵入性指针和非侵入性指针之间有几个重要区别:

第二(非侵入性)的最大优势是:

  • 实现对第二个引用(即shared_ptr/weak_ptr)的弱引用要简单得多。

第一个优点是当您需要获得智能指针时(至少在boost::shared_ptrstd::tr1::shared_ptr的情况下)。

  • 不能在构造函数和析构函数中使用shared_ptr
  • 在类的层次结构中使用shared_from是非常重要的。
票数 8
EN

Stack Overflow用户

发布于 2010-03-21 13:50:27

首先,我要提醒您,共享所有权通常是一种难以驯服的野兽,并且可能导致很难根除bug。

有许多方法可以避免共享所有权。Factory方法(使用Boost指针容器实现自身)是我个人最喜欢的方法之一。

现在,关于参考点票.

1.侵入性指针

计数器嵌入在对象本身中,这意味着:

  • 您需要提供向计数器添加/减字符串的方法,并且您有责任使它们成为线程安全的
  • 计数不能存活于对象,所以没有weak_ptr,所以如果不使用Observer模式,您就不能在设计中有循环的引用。相当复杂

2.非侵入性指针

我只谈boost::shared_ptrboost::weak_ptr。最近,我对源代码进行了一些深入的研究,以精确地了解机械原理,而且确实有更复杂的事情要比上面的更复杂!

代码语言:javascript
复制
// extract of <boost/shared_ptr.hpp>

template <class T>
class shared_ptr
{
  T * px;                     // contained pointer
  boost::detail::shared_count pn;    // reference counter
};
  • 计数的维护已经为您写好了,并且是线程安全的。
  • 在循环引用的情况下,可以使用weak_ptr
  • 只有一个构建shared_ptr对象的对象需要了解对象析构函数(参见示例)

下面是一个小小的例子,来说明这个向前声明的魔力:

代码语言:javascript
复制
 // foofwd.h
 #include <boost/shared_ptr.hpp>

 class Foo;

 typedef boost::shared_ptr<Foo> foo_ptr;

 foo_ptr make_foo();

 // foo.h
 #include "foofwd.h"

 class Foo { /** **/ };

 // foo.cpp
 #include "foo.h"

 foo_ptr make_foo() { return foo_ptr(new Foo()); }

 // main.cpp
 #include "foofwd.h"

 int main(int argc, char* argv[])
 {
   foo_ptr p = make_foo();
 } // p.get() is properly released

有一点模板魔术来授权这一点。基本上,计数器对象嵌入了一个允许进行某种类型擦除的disposer* (但是是第三个分配)。但是非常有用,因为它确实允许转发声明!

3.结论

虽然我同意侵入性指针可能更快,因为在较少的分配中(为shared_ptr分配了3个不同的内存块),但也没有那么实用。

因此,我想向您指出Boost侵入指针库,特别是它的介绍:

通常情况下,如果intrusive_ptr是否比shared_ptr更适合您的需求并不明显,那么首先尝试shared_ptr-based设计。

票数 7
EN

Stack Overflow用户

发布于 2010-03-21 09:47:24

我不知道一项关于非侵入性过度侵入造成的额外成本的研究。但我要指出的是,非侵入性似乎是C++专家普遍推荐的.当然,这可能没什么意义!但理由很合理:如果你需要智能指针,那是因为你想要一种更简单的方法来实现对象生命周期管理,而不是手工编写它,所以你强调的是正确性和简单性,而不是性能,这总是一个好主意,直到你描述了一个真实的设计模型。

很可能是在一个简化的测试中,非侵入性的速度是侵入性的两倍,然而在实际工作的实际程序中,这种速度差异在噪音中消失了,变得如此微不足道,以至于你甚至无法测量它。这是一种非常普遍的现象;您想象的对性能有重要意义的事情通常并不重要。

如果您发现了性能瓶颈,这是可能的(可能吗?)维护引用计数本身的工作(在这两种方法中)对性能的影响与非侵入性方法中的额外间接影响一样大。对于原始指针,语句:

代码语言:javascript
复制
p1 = p2;

在优化器发挥了作用之后,可能只需要在两个CPU寄存器之间移动一个值。但是,如果它们是引用来计算智能指针,即使具有侵入性,这就像:

代码语言:javascript
复制
if (p1 != p2)
{
    if ((p1 != 0) && (--(p1->count) == 0))
        delete p1;

    p1 = p2;

    if (p1 != 0)
        p1->count++;
}

这种情况发生在传递给每个函数的每个智能指针参数中。因此,有许多额外的访问可能遥远的区域的记忆,以提高和降低计数每次。为了确保线程安全,增量和递减检查操作必须是互锁/原子操作,这可能对多核产生严重的负面影响。

我认为C++的“亮点”是这样一种情况,即您不需要管理这种非常动态的数据结构。相反,您有一个简单的对象所有权分层模式,因此每个对象都有一个明显的单一所有者,而且数据的生存期往往遵循函数调用的生存期(通常不是这样)。然后,您可以让标准容器和函数调用堆栈为您管理一切。这一点在即将推出的带有rvalue引用、unique_ptr等的语言版本中得到了强调,这一切都是为了以一种简单的方式围绕对象的单一所有权进行转移。如果您真的需要动态的多所有者生命周期管理,那么真正的GC将更快、更容易正确使用,但是对于GC来说,C++并不是一个非常愉快的家。

另一个小问题:不幸的是,“在非侵入式智能指针中,只有一个指针指向每个Foo”是不正确的。在Foo内部有一个this指针,它是一个Foo *,所以裸露的指针仍然可以泄漏出去,通常是很难找到的。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2486493

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档