在过去的几年里,我普遍接受了这一点
如果我要使用ref-counting智能指针
入侵智能指针是要走的路
-
但是,由于以下原因,我开始喜欢非侵入式智能指针:
这个Foo在非侵入式智能指针中移动物体比侵入式智能指针更容易的唯一原因是:
在非侵入式智能指针中,只有一个指针指向每个Foo.
在入侵智能指针中,我不知道有多少对象指向每个Foo.
现在,非侵入式智能指针的唯一成本是双重间接.[也许这搞砸了缓存].
有没有人对昂贵的这个额外的间接层进行了很好的研究?
编辑:通过智能指针,我可能会指其他人称之为"共享指针"; 整个想法是:有一个附加到对象的引用计数,当它到达0时,该对象被自动删除
侵入性或非侵入性指针之间存在几个重要区别:
第二(非侵入性)的最大优势:
shared_ptr/ weak_ptr)的弱引用要简单得多.第一个优势是,当你需要获得智能指针在此(至少在以下情况下boost::shared_ptr,std::tr1::shared_ptr)
shared_ptr在构造函数和析构函数中使用它.好吧,首先,我要提醒你,共享所有权通常是一个难以驯服的野兽,并且可能导致很难根除错误.
有许多方法不共享所有权.这种Factory方法(使用Boost指针容器实现)是我个人最喜欢的方法之一.
现在,至于引用计数......
1.侵入式指针
计数器嵌入在对象本身中,这意味着:
weak_ptr,所以你不能在没有使用Observer模式的情况下在你的设计中使用引用循环......非常复杂2.非侵入式指针
我只会谈论boost::shared_ptr和boost::weak_ptr.我最近挖了一点来精确地看一下这些机制,确实上面的内容确实复杂得多!
// extract of <boost/shared_ptr.hpp>
template <class T>
class shared_ptr
{
T * px; // contained pointer
boost::detail::shared_count pn; // reference counter
};
Run Code Online (Sandbox Code Playgroud)
weak_ptr在循环引用的情况下使用.shared_ptr对象的对象需要知道对象析构函数(参见示例)这是一个小例子,来说明这个前瞻宣言的魔力:
// 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
Run Code Online (Sandbox Code Playgroud)
授权这个有一点模板魔法.基本上,计数器对象嵌入disposer*(允许第三种分配),允许进行某种类型的擦除.真的很有用,因为它真的允许前向声明!
3.结论
虽然我同意Intrusive Pointers可能更快,因为正在进行更少的分配(为a分配了3个不同的内存块shared_ptr),但实际上也不太常见.
所以我想指出Boost Intrusive Pointer库,特别是它的介绍:
作为一般规则,如果不能
intrusive_ptr更好地满足您的需求shared_ptr,请首先尝试shared_ptr基于设计.
我不了解因非侵入性而不是侵入性而导致的额外费用的研究。但是我要指出,C ++专家普遍建议使用非侵入性技术。当然,这可能没有任何意义!但是推理很合理:如果您需要智能指针,那是因为您想要一种简单的方法来实现对象生命周期管理,而不是手工编写,因此您强调的是正确性和简单性,而不是性能,这一直是一个好主意,除非您剖析了整个设计的真实模型。
很有可能在简化的测试中,非侵入性的速度是侵入性的两倍,但是在执行实际工作的真实程序中,这种速度差异会在噪声中消失,并且变得微不足道,甚至无法测量它。这是很普遍的现象。您想象中对性能至关重要的事情通常并不重要。
如果发现性能瓶颈,则很可能(可能是?)维护引用计数本身(在两种方法中)对性能的影响与在非侵入性方法中的额外间接影响一样大。使用原始指针,该语句:
p1 = p2;
Run Code Online (Sandbox Code Playgroud)
优化器发挥作用后,可能只需要在两个CPU寄存器之间移动一个值。但是,如果它们引用计数智能指针,即使具有侵入性,它也像:
if (p1 != p2)
{
if ((p1 != 0) && (--(p1->count) == 0))
delete p1;
p1 = p2;
if (p1 != 0)
p1->count++;
}
Run Code Online (Sandbox Code Playgroud)
传递给每个函数的每个智能指针参数都会发生这种情况。因此,有很多额外的访问权限来访问潜在的遥远的内存区域,以每次增加和减少计数。而且,为了确保线程安全,增量和减量检查操作必须互锁/原子操作,这可能会对多个内核产生严重的负面影响。
我认为C ++的“最佳结合点”是您不需要管理这种疯狂的动态数据结构的情况。相反,您具有对象所有权的简单分层模式,因此每个对象都有一个明显的单一所有者,并且数据的生存期倾向于遵循函数调用的生存期(通常不是)。然后,您可以让标准容器和函数调用堆栈为您管理一切。在即将发行的带有右值引用unique_ptr等的语言版本中对此进行了强调,这一切都是关于以一种简单的方式围绕对象的单所有权进行转移的。如果您确实需要动态的多所有者生命周期管理,那么真正的GC将更快, 更容易正确使用,但是C ++对于GC来说不是一个很幸福的家。
另一个小问题:不幸的是,“在非侵入式智能指针中,只有一个指向每个Foo的指针”是不正确的。里面Foo是一个this指针,Foo *所以裸露的指针仍然可以泄漏,通常以很难发现的方式泄漏出去。
| 归档时间: |
|
| 查看次数: |
4753 次 |
| 最近记录: |