当前位置:  开发笔记 > 编程语言 > 正文

使用boost :: shared_ptr时有什么潜在的危险?

如何解决《使用boost::shared_ptr时有什么潜在的危险?》经验,为你挑选了6个好方法。

使用时,你有什么方法可以用脚射击自己boost::shared_ptr?换句话说,当我使用时,我必须避免哪些陷阱boost::shared_ptr



1> Kaz Dragon..:

循环引用:shared_ptr<>对于具有shared_ptr<>原始对象的内容.weak_ptr<>当然,你可以用来打破这个循环.


我在评论中添加以下内容作为我所谈论的内容的一个例子.

class node : public enable_shared_from_this {
public :
    void set_parent(shared_ptr parent) { parent_ = parent; }
    void add_child(shared_ptr 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 parent_;
    vector< shared_ptr > children_;
};

在此示例中,您有一个节点树,每个节点都包含指向其父节点的指针.无论出于何种原因,frob()成员函数都会向上穿过树.(这不完全是古怪的;一些GUI框架以这种方式工作).

问题是,如果您失去对最顶层节点的引用,那么最顶层的节点仍然拥有对其子节点的强引用,并且其所有子节点也对其父节点具有强引用.这意味着存在循环引用,使所有实例不能自行清理,而实际上无法从代码中到达树,这个内存泄漏.

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

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

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

这里,父节点已被弱指针替换.它在它所引用的节点的生命周期中不再有发言权.因此,如果最顶层的节点超出了前一个示例中的范围,那么虽然它拥有对其子节点的强引用,但它的子节点并不强烈引用其父节点.因此,对该对象没有强烈的引用,它会清理自己.反过来,这会导致孩子失去一个强大的参考,导致他们清理,等等.简而言之,这不会泄漏.并且通过战略性地用weak_ptr <>替换shared_ptr <>.

注意:上面同样适用于std :: shared_ptr <>和std :: weak_ptr <>,就像它对boost :: shared_ptr <>和boost :: weak_ptr <>一样.


@Phil,是的,这是我对设计弱点的烦恼:在堆上创建对象时不考虑数据的所有权.在多线程项目中它变得更糟.
@curiousguy我不确定你遇到的问题究竟是什么.只需用weak_ptr <>替换一个shared_ptr <>,这就打破了循环依赖.如果你能详细说明你遇到的问题,我会很乐意为你改进我的答案.
@curious:有趣的是,出于这个原因,我已经用乘法替换了除法."绝对无意义"和"过度简化"是两个完全不同的东西.
@curiousguy我偶然在别处读了你的另一个答案,我想我理解你的观点.CMIIW:你认为没有循环依赖,可以证明你可以将weak_ptr置于首位.我发现这无益 - 然后就是没有循环依赖这样的东西:对象都是独立构造的.因此,让我们以一种有用的方式定义"循环依赖",即"让对象处于它们具有共同依赖的生命周期的状态".我认为这是合理的.因此,我的答案来自原始问题.

2> Michael Burr..:

shared_ptr为同一个对象创建多个不相关的:

#include 
#include "boost/shared_ptr.hpp"

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

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

typedef boost::shared_ptr 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;
}


请参阅boost :: make_shared()以完全避免新的.:)
当然,这里的解决方案是永远不要在`shared_ptr`构造函数之外使用`new`.

3> Brian Campbe..:

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

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

这是因为允许new Foo()执行,然后g()调用,并g()抛出异常,而无需shared_ptr设置,因此shared_ptr没有机会清理Foo对象.



4> 小智..:

小心地将两个指针指向同一个对象.

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

b->doSomething(); // crashes

而是使用这个

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

b->doSomething(); // no crash

此外,任何持有shared_ptrs的类都应该定义复制构造函数和赋值运算符.

不要尝试在构造函数中使用shared_from_this() - 它将不起作用.而是创建一个静态方法来创建类并让它返回一个shared_ptr.

我已经毫无困难地传递了对shared_ptrs的引用.只需确保在保存之前复制它(即没有作为类成员的引用).


>"此外,任何持有shared_ptrs的类都应该定义复制构造函数和赋值运算符." ----为什么?是不是使用智能指针减少复制ctor和赋值运算符的需要?

5> Frank..:

这有两件事要避免:

调用get()函数获取原始指针并在指向对象超出范围后使用它.

将引用或原始指针传递给a也shared_ptr应该是危险的,因为它不会增加内部计数,这有助于保持对象存活.



6> Mykola Golub..:

我们调试了几周奇怪的行为.

原因是:
我们将'this'传递给了一些线程工作者而不是'shared_from_this'.

推荐阅读
携手相约幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有