都有些什么方法你可以搬起石头砸自己的脚时使用 boost::shared_ptr?换句话说,有什么陷阱我必须避免当我用 boost::shared_ptr?

有帮助吗?

解决方案

循环引用:一个shared_ptr<>到东西,有一个shared_ptr<>原始对象。您可以使用weak_ptr<>来打破这种恶性循环,当然。


我增加下面就是我在评论中谈到的一个例子。

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        if (parent_) parent_->frob();
    }

private :
    void do_frob();
    shared_ptr<node> parent_;
    vector< shared_ptr<node> > children_;
};

在本例中,有节点的树,其中每一个有一个指针指向其父。所述FROB()成员函数,无论出于何种原因,波纹向上通过树。 (这并不完全古怪;一些GUI框架以这种方式工作)。

的问题是,如果你失去了参照最上面的节点,然后在最上面的节点仍持有其子强引用,其所有的孩子也抱持着强烈的参考他们的父母。这意味着,有循环引用保持所有实例从清洗而上,而没有任何的实际从代码到达树,此内存泄漏的方法。

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()
        if (parent) parent->frob();
    }

private :
    void do_frob();
    weak_ptr<node> parent_; // Note: now a weak_ptr<>
    vector< shared_ptr<node> > children_;
};

这里,父节点已经被替换为弱指针。它不再具有它所引用的节点的寿命有发言权。因此,如果最顶端的节点超出范围,因为在前面的例子,那么当它持有其子强引用,它的孩子,不要将他们的父母强引用。因此,有对象没有强引用,并清除自己的身体。反过来,这会导致孩子失去一个有力的参考,这将导致他们清理,等等。总之,这不会泄漏。并且只是通过用的weak_ptr策略性地更换一个shared_ptr <> <>

注:上述同样适用于标准:: shared_ptr的<>和std ::的weak_ptr <>因为它提高:: shared_ptr的<>和boost ::的weak_ptr <>

其他提示

创建多个无关shared_ptr的相同的对象:

#include <stdio.h>
#include "boost/shared_ptr.hpp"

class foo
{
public:
    foo() { printf( "foo()\n"); }

    ~foo() { printf( "~foo()\n"); }
};

typedef boost::shared_ptr<foo> pFoo_t;

void doSomething( pFoo_t p)
{
    printf( "doing something...\n");
}

void doSomethingElse( pFoo_t p)
{
    printf( "doing something else...\n");
}

int main() {
    foo* pFoo = new foo;

    doSomething( pFoo_t( pFoo));
    doSomethingElse( pFoo_t( pFoo));

    return 0;
}

构造的匿名临时共享指针,例如参数内的函数调用:

f(shared_ptr<Foo>(new Foo()), g());

这是因为它是允许的要执行的new Foo(),然后g()调用,g()抛出一个异常,而没有被建立shared_ptr,所以shared_ptr没有机会清理Foo对象。

小心使两个指针到相同的对象。

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d( b.get() );
} // d goes out of scope here, deletes pointer

b->doSomething(); // crashes

代替使用此

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d = 
    boost::dynamic_pointer_cast<Derived,Base>( b );
} // d goes out of scope here, refcount--

b->doSomething(); // no crash

此外,任何保持shared_ptrs应该定义拷贝构造和赋值操作符的类。

不要尝试使用shared_from_this()在构造 - 它不会工作。代替创建一个静态方法来创建类并将它返回一个shared_ptr。

我通过引用shared_ptrs没有麻烦。只要确保它被复制它的保存之前(即,作为类成员的引用)。

这里有两件事情,以避免:

  • get() 功能得到的原始指针和后使用它的对象超出范围。

  • 通过一个基准或者原的指针 shared_ptr 应该是危险的,因为它不会增加内部数,这有助于保持对象活着。

我们调试几个星期奇怪的行为。

<强>的原因是:结果 我们通过“这个”一些线索工人,而不是“shared_from_this”。

不正是一个footgun,但可以肯定的挫折,直到环绕如何做到这一点的C ++ 0x的方式你的头源:最谓词你知道从<functional>爱不shared_ptr发挥很好。令人高兴的是,std::tr1::mem_fn与工作对象,指针和shared_ptrs,更换std::mem_fun,但如果你想使用std::negatestd::not1std::plus或任何的那些老朋友shared_ptr,准备让舒适与std::tr1::bind,可能一个参数占位符为好。在实践中,这实际上是很多更通用,因为现在你基本上最终使用bind为每个函数对象适配器,但它确实需要一些时间来适应,如果你已经熟悉了STL的便利功能。

该DDJ文章触及的主题,有许多的例子的代码。我也几年前博客了解它时,我首先必须弄清楚如何做到这一点

使用了非常小的物体(如shared_ptr charshort如果你有很多上堆的小物件,但他们不是真正“共享”,可能是一个开销。 boost::shared_ptr分配16个字节为每一个新的参考计数其与升压1.42上创建克++ 4.4.3和VS2008。 std::tr1::shared_ptr分配20个字节。现在,如果你有一百万个不同shared_ptr<char>该装置20个百万字节内存的只持有数= 1都没有了。且不说间接成本和内存碎片。试着用你最喜欢的平台下面。

void * operator new (size_t size) {
  std::cout << "size = " << size << std::endl;
  void *ptr = malloc(size);
  if(!ptr) throw std::bad_alloc();
  return ptr;
}
void operator delete (void *p) {
  free(p);
}

给予了一个shared_ptr 这个类定义中也是危险的。 使用enabled_shared_from_this代替。

这里参见下面的交

您需要的时候你在多线程代码中使用shared_ptr要小心。它然后相对容易当夫妇shared_ptrs的,指向相同的存储器中,使用由不同的线程变得到的情况。

在流行普遍使用的shared_ptr的几乎不可避免地导致不希望的和看不见的存储器占用。

循环引用是一个公知的原因,它们中的一些可以是间接的并且很难被发现尤其是在复杂的代码是由一个以上的程序员制作;程序员可以决定不止一个对象需要另一个的引用,速战速决,没有时间检查所有代码,看看他是否关闭循环。这种危险被无限低估。

少很好理解是未发布的参考文献的问题。如果一个对象被共享出来很多shared_ptrs那么就不会被破坏,直到他们中的每一个归零或超出范围。这是很容易忽视这些引用的一个,并与你以为你已经完成了与对象存储潜伏着看不见的结束。

虽然严格来说这些不是内存泄漏(它将所有在程序退出之前被释放)它们只是作为有害和更难以检测。

这些问题的权宜之计虚假声明的后果:1,声明你真的想成为一个所有权的shared_ptr什么。 scoped_ptr的是正确的,但随后其他任何引用该对象必须是原始指针,可以保留为悬空。 2.声明你真的想成为一个被动的观测基准为shared_ptr的是什么。 weak_ptr的是正确的,但那么你必须将其转换成share_ptr你想用它每一次的麻烦。

我怀疑你的项目是什么样的麻烦,这种做法可以让你成为一个很好的例子。

如果你有一个内存密集型应用程序,你真的需要单一的所有权,使您的设计可以明确控制对象的寿命。

使用单所有权opObject = NULL;肯定会删除对象,它现在会做的。

使用共享所有权spObject = NULL; ........谁知道?......

如果你有一个注册的共享的对象(一个列表中的所有活动实例,例如),对象将永远不会被释放。方案:作为在这种情况下的循环依赖结构(见Kaz龙的答复),使用weak_ptr为合适。

智能指针不是一切,和原始指针不能被消除

可能是最糟糕的危险是,由于shared_ptr是一个有用的工具,人们将开始把它的每一个地方。由于普通的指针也可能被滥用,同样的人会寻找原始指针,并尝试用绳子,容器或智能指针来代替他们,即使它是没有意义的。原始指针的合法使用将成为犯罪嫌疑人。会有一个指针警察。

这不仅是可能是最糟糕的危险,它可能是唯一严重的危险。所有shared_ptr的最严重的侵权行为将是思想的直接后果是智能指针均优于原始指针(这意味着什么),并且把智能指针到处将使C ++程序设计“更安全”。

当然这一事实本身智能指针需要被转换到原始指针是用于反驳该权利要求智能指针崇拜的,但事实上,在原始指针连接在operator*operator->的“隐式”(或在get()明确的),但在一个隐式转换不是隐含的,足以给人的印象是,这是不是一个真正的转换,并且,通过该非转换产生的原始指针是无害的临时

C ++不能进行“安全语言”,和C的任何有用的子集++是“安全”的

当然追求安全子集(“安全”在“存储器安全”的严格意义上,如LISP,Haskell中,爪哇...)C ++的注定是无止境的,不令人满意的,因为安全子集C ++是微小的,几乎是无用的,因为不安全元是规则而不是例外。在C ++严格存储器安全意味着没有指针和仅具有自动存储类的引用。但在程序员定义信任的语言,有些人会坚持使用一些(原则上)傻瓜型“智能指针”,即使有超过了<原始指针没有其他优势EM>一个特定的方式螺丝避免了程序的状态。

scroll top